跳到主要內容

變數

這是一個建立和初始化變數的示例

dart
var name = 'Bob';

變數儲存引用。名為 name 的變數包含對值為“Bob”的 String 物件的引用。

name 變數的型別被推斷為 String,但您可以透過明確指定來更改該型別。如果一個物件不限於單一型別,請指定 Object 型別(如果需要,也可指定 dynamic)。

dart
Object name = 'Bob';

另一種選擇是顯式宣告將被推斷的型別

dart
String name = 'Bob';

空安全

#

Dart 語言強制執行健全的空安全。

空安全可防止因意外訪問設定為 null 的變數而導致的錯誤。此錯誤稱為空解引用錯誤。當您在評估為 null 的表示式上訪問屬性或呼叫方法時,會發生空解引用錯誤。此規則的例外情況是 null 支援該屬性或方法,例如 toString()hashCode。透過空安全,Dart 編譯器在編譯時檢測這些潛在錯誤。

例如,假設您想查詢一個 int 變數 i 的絕對值。如果 inull,則呼叫 i.abs() 會導致空解引用錯誤。在其他語言中,嘗試這樣做可能會導致執行時錯誤,但 Dart 的編譯器禁止這些操作。因此,Dart 應用程式不會導致執行時錯誤。

空安全引入了三個關鍵變更

  1. 當您為變數、引數或其他相關元件指定型別時,可以控制該型別是否允許 null。要啟用可空性,您需要在型別宣告的末尾新增一個 ?

    dart
    String? name  // Nullable type. Can be `null` or string.
    
    String name   // Non-nullable type. Cannot be `null` but can be string.
  2. 您必須在使用變數之前對其進行初始化。可空變數預設為 null,因此它們預設會被初始化。Dart 不為不可空型別設定初始值。它強制您設定初始值。Dart 不允許您觀察未初始化的變數。這可以防止您訪問接收者型別可能為 nullnull 不支援所使用的方法或屬性的屬性或呼叫方法。

  3. 您不能訪問可空型別表示式上的屬性或呼叫方法。同樣的例外也適用於 null 支援的屬性或方法,例如 hashCodetoString()

健全的空安全將潛在的執行時錯誤轉化為編輯時分析錯誤。當不可空變量出現以下情況時,空安全會對其進行標記:

  • 未用非空值初始化。
  • 被賦予了 null 值。

此檢查允許您在部署應用程式之前修復這些錯誤。

預設值

#

具有可空型別的未初始化變數的初始值為 null。即使是數字型別的變數最初也是空值,因為數字(像 Dart 中的所有其他事物一樣)都是物件。

dart
int? lineCount;
assert(lineCount == null);

使用空安全時,您必須在使用不可空變數之前初始化其值

dart
int lineCount = 0;

您不必在宣告區域性變數時就對其進行初始化,但您需要在其使用之前為其賦值。例如,以下程式碼是有效的,因為 Dart 可以檢測到 lineCount 在傳遞給 print() 時是非空的

dart
int lineCount;

if (weLikeToCount) {
  lineCount = countLines();
} else {
  lineCount = 0;
}

print(lineCount);

頂層變數和類變數是延遲初始化的;初始化程式碼在變數首次使用時執行。

Late 變數

#

late 修飾符有兩種用例

  • 宣告一個在聲明後初始化的不可空變數。
  • 延遲初始化變數。

通常,Dart 的控制流分析可以檢測到不可空變數在使用前何時被設定為非空值,但有時分析會失敗。兩種常見情況是頂層變數和例項變數:Dart 通常無法確定它們是否已設定,因此它不會嘗試。

如果您確定變數在使用前已設定,但 Dart 不同意,則可以透過將變數標記為 late 來修復該錯誤

dart
late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

當您將變數標記為 late 但在宣告時對其進行初始化時,初始化器會在變數首次使用時執行。這種延遲初始化在以下幾種情況下非常方便

  • 變數可能不需要,並且初始化它成本很高。
  • 您正在初始化一個例項變數,並且其初始化器需要訪問 this

在以下示例中,如果 temperature 變數從未使用過,那麼開銷大的 readThermometer() 函式將永遠不會被呼叫

dart
// This is the program's only call to readThermometer().
late String temperature = readThermometer(); // Lazily initialized.

Final 和 const

#

如果您不打算更改變數,請使用 finalconst,可以替代 var,也可以與型別一起使用。final 變數只能設定一次;const 變數是編譯時常量。(const 變數隱式為 final。)

這是一個建立和設定 final 變數的示例

dart
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

您不能更改 final 變數的值

✗ 靜態分析:失敗dart
name = 'Alice'; // Error: a final variable can only be set once.

對您希望成為編譯時常量的變數使用 const。如果 const 變數在類級別,請將其標記為 static const。在宣告變數的地方,將其值設定為編譯時常量,例如數字或字串字面量、const 變數,或常量數字上的算術運算結果

dart
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere

const 關鍵字不僅用於宣告常量變數。您還可以用它來建立常量,以及宣告建立常量值的建構函式。任何變數都可以擁有常量值。

dart
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

您可以從 const 宣告的初始化表示式中省略 const,如上面 baz 所示。有關詳細資訊,請參閱 不要冗餘使用 const

您可以更改非 final、非 const 變數的引用,即使它曾經具有 const

dart
foo = [1, 2, 3]; // Was const []

您不能更改 const 變數的值

✗ 靜態分析:失敗dart
baz = [42]; // Error: Constant variables can't be assigned a value.

您可以定義使用型別檢查和轉換isas)、集合 if展開運算子......?)的常量

dart
const Object i = 3; // Where i is a const Object with an int value...
const list = [i as int]; // Use a typecast.
const map = {if (i is int) i: 'int'}; // Use is and collection if.
const set = {if (list is List<int>) ...list}; // ...and a spread.

有關使用 const 建立常量值的更多資訊,請參閱列表對映

萬用字元變數

#

名稱為 _ 的萬用字元變數聲明瞭一個非繫結的區域性變數或引數;本質上,它是一個佔位符。如果存在初始化器,它仍會執行,但值不可訪問。在同一名稱空間中可以存在多個名為 _ 的宣告,而不會發生命名衝突錯誤。

頂層宣告或可能影響庫隱私的成員不適用於萬用字元變數。塊範圍內的區域性宣告,例如以下示例,可以宣告萬用字元

  • 區域性變數宣告。

    dart
    main() {
      var _ = 1;
      int _ = 2;
    }
  • for 迴圈變數宣告。

    dart
    for (var _ in list) {}
  • catch 子句引數。

    dart
    try {
      throw '!';
    } catch (_) {
      print('oops');
    }
  • 泛型和函式型別引數。

    dart
    class T<_> {}
    void genericFunction<_>() {}
    
    takeGenericCallback(<_>() => true);
  • 函式引數。

    dart
    Foo(_, this._, super._, void _()) {}
    
    list.where((_) => true);
    
    void f(void g(int _, bool _)) {}
    
    typedef T = void Function(String _, String _);