目錄

詞彙表

以下是 Dart 文件中使用的術語定義。

輔助

#

輔助是一種自動化的本地程式碼編輯,旨在對程式碼進行常見的改進。輔助的範例包括將 switch 陳述式轉換為 switch 運算式、反轉 if 陳述式中的 thenelse 區塊,以及將小工具插入小工具結構中。

相關文件和資源

常數上下文

#

常數上下文是指程式碼的區域,其中不需要包含 const 關鍵字,因為它隱含在該區域中的所有內容都必須是常數的事實中。以下位置是常數上下文

  • const 關鍵字為前綴的清單、映射或集合文字內的所有內容。例如

    dart
    var l = const [/*constant context*/];
  • 常數建構子調用內的引數。例如

    dart
    var p = const Point(/*constant context*/);
  • const 關鍵字為前綴的變數的初始化式。例如

    dart
    const v = /*constant context*/;
  • 註解。

  • case 子句中的運算式。例如

    dart
    void f(int e) {
      switch (e) {
        case /*constant context*/:
          break;
      }
    }

相關文件和資源

明確賦值

#

明確賦值分析是確定程式碼中每個點的每個區域變數,以下哪個為真的過程

  • 變數已經明確地賦予一個值(明確賦值)。
  • 變數已經明確地未賦予一個值(明確未賦值)。
  • 變數可能已賦予值,也可能沒有,取決於到達該點所採取的執行路徑。

明確賦值分析有助於找出程式碼中的問題,例如可能未賦予值的變數被引用的位置,或只能賦予值一次的變數在可能已賦予值後被賦予值的位置。

例如,在以下程式碼中,當變數 s 作為引數傳遞給 print 時,它是明確未賦值的

dart
void f() {
  String s;
  print(s);
}

但在以下程式碼中,變數 s 是明確賦值的

dart
void f(String name) {
  String s = 'Hello $name!';
  print(s);
}

當有多個可能的執行路徑時,明確賦值分析甚至可以判斷變數是否已明確賦值(或未賦值)。在以下程式碼中,如果執行通過 if 陳述式的 true 或 false 分支,則會呼叫 print 函式,但因為無論採取哪個分支都會賦予 s,所以它在傳遞給 print 之前是明確賦值的

dart
void f(String name, bool casual) {
  String s;
  if (casual) {
    s = 'Hi $name!';
  } else {
    s = 'Hello $name!';
  }
  print(s);
}

在流程分析中,if 陳述式的結尾稱為聯結—兩個或多個執行路徑合併在一起的位置。在有聯結的地方,如果變數在所有合併的路徑上都是明確賦值的,則分析會說該變數是明確賦值的,如果變數在所有路徑上都是明確未賦值的,則分析會說該變數是明確未賦值的。

有時變數會在一個路徑上賦予值,但在另一個路徑上則不會,在這種情況下,變數可能已賦予值,也可能沒有。在以下範例中,if 陳述式的 true 分支可能執行,也可能不執行,因此變數可能已賦予值,也可能沒有

dart
void f(String name, bool casual) {
  String s;
  if (casual) {
    s = 'Hi $name!';
  }
  print(s);
}

如果存在未將值賦予 s 的 false 分支,情況也是如此。

迴圈的分析稍微複雜一些,但它遵循相同的基本推理。例如,while 迴圈中的條件始終執行,但主體可能執行,也可能不執行。因此,就像 if 陳述式一樣,在 while 陳述式的結尾,在條件為 true 的路徑和條件為 false 的路徑之間存在一個聯結。

相關文件和資源

函式

#

用於指稱頂層函式、本地函式、靜態方法和實例方法的總稱。

相關文件和資源

不可反駁的模式

#

不可反駁的模式是始終匹配的模式。不可反駁的模式是唯一可以出現在不可反駁的上下文中的模式:宣告賦值模式上下文。

相關文件和資源

混入應用

#

混入應用是在將混入應用於類別時建立的類別。例如,考慮以下宣告

dart
class A {}

mixin M {}

class B extends A with M {}

類別 BMA 的混入應用程式的子類別,有時也稱為 A+M。類別 A+MA 的子類別,並且具有從 M 複製而來的成員。

您可以將混入應用程式定義為

dart
class A {}

mixin M {}

class A_M = A with M;

在給定 A_M 的宣告後,以下 B 的宣告與原始範例中 B 的宣告相同

dart
class B extends A_M {}

相關文件和資源

覆寫推論

#

覆寫推論是根據它覆寫的方法或方法中對應的類型來推斷方法宣告中任何遺失的類型的過程。

如果候選方法(缺少類型資訊的方法)覆寫了單一繼承的方法,則會推斷出被覆寫的方法中的對應類型。例如,請考慮以下程式碼

dart
class A {
  int m(String s) => 0;
}

class B extends A {
  @override
  m(s) => 1;
}

B 中的 m 宣告是一個候選,因為它缺少回傳類型和參數類型。因為它覆寫了單一方法(A 中的方法 m),所以會使用被覆寫的方法中的類型來推斷遺失的類型,並且會如同 B 中的方法已宣告為 int m(String s) => 1; 一樣。

如果候選方法覆寫了多個方法,並且其中一個被覆寫的方法 Ms 的函式類型是所有其他被覆寫的方法的函式類型的超類型,則使用 Ms 來推斷遺失的類型。例如,請考慮以下程式碼

dart
class A {
  int m(num n) => 0;
}

class B {
  num m(int i) => 0;
}

class C implements A, B {
  @override
  m(n) => 1;
}

C 中的 m 宣告是一個用於覆寫推論的候選,因為它缺少回傳類型和參數類型。它覆寫了 A 中的 mB 中的 m,因此編譯器需要從中選擇一個可以推斷遺失類型的方法。但是,由於 Am 的函式類型 (int Function(num)) 是 Bm 的函式類型 (num Function(int)) 的超類型,因此會使用 A 中的函式來推斷遺失的類型。結果與將 C 中的方法宣告為 int m(num n) => 1; 相同。

如果沒有任何被覆寫的方法的函式類型是所有其他被覆寫的方法的超類型,則會發生錯誤。

相關文件和資源

部分檔案

#

部分檔案是一個 Dart 原始程式碼檔案,其中包含 part of 指令,並使用 part 指令包含在函式庫中。

相關文件和資源

可能為非空值

#

如果類型是明確非空值或類型參數,則該類型可能為非空值

如果類型名稱後沒有問號 (?),則該類型是明確非空值。請注意,有一些類型始終可為空值,例如 Nulldynamic,並且只有在 FutureOr 後面沒有問號類型引數為非空值時 (例如 FutureOr<String>) 才為非空值。

類型參數可能為非可空類型,因為實際的執行階段類型(指定為類型引數的類型)可能為非可空。例如,給定 class C<T> {} 的宣告,C 類型可以使用非可空類型引數,例如 C<int>

相關文件和資源

公共函式庫

#

公開程式庫是指位於套件的 lib 目錄內,但不在 lib/src 目錄內的程式庫。

相關文件和資源

快速修正

#

自動化的本地程式碼編輯,旨在修正特定診斷回報的問題。

相關文件和資源

重構

#

重構是指旨在修改非本地程式碼或需要使用者互動的程式碼編輯。重構的範例包括重新命名、移除或提取程式碼。

相關文件和資源

可反駁的模式

#

可反駁模式是一種可以針對值進行測試,以判斷模式是否與該值匹配的模式。如果沒有,則模式會反駁或拒絕匹配。可反駁模式出現在匹配上下文中。

相關文件和資源

子類別

#

子類別是指透過使用 extends 關鍵字或透過混合應用來繼承另一個類別實作的類別。

dart
// A is a subclass of B; B is the superclass of A.
class A extends B {}

// B1 has the superclass `A with M`, which has the superclass A.
class B1 extends A with M {}

子類別關係也表示相關的子類型關係。例如,class A 隱式定義了相關的類型 AA 類別的實例會使用此類型。因此,class A extends B 不僅宣告了 A 類別是 B 的子類別,也確立了類型 A 是類型 B子類型

子類別關係是子類型關係的子集。當文件指出「S 必須是 T 的子類型」時,S 作為 T 的子類別是沒問題的。但是,反之則不然:並非所有子類型都是子類別。

相關文件和資源

子類型

#

子類型關係是指某個類型的值可以替換為預期另一個類型(超類型)的值。例如,如果 ST 的子類型,那麼您可以在預期類型為 T 的值時替換類型為 S 的值。

子類型支援其超類型的所有操作(並且可能支援一些額外操作)。實際上,這表示您可以將子類型的值指派給任何預期超類型的位置,並且超類型所有的方法都可在子類型上使用。

至少在靜態上是如此。根據其操作,特定的 API 可能不允許在執行階段進行替換。

某些子類型關係基於類型的結構,例如可空類型(例如,intint? 的子類型)和函式類型(例如,String Function()void Function() 的子類型)。

也可以透過實作繼承(直接或間接)為類別引入子類型

dart
// A is a subtype of B, but NOT a subclass of B.
class A implements B {}

// C is a subtype AND a subclass of D.
class C extends D {}

相關文件和資源

變異數和變異數位置

#

當類別(或其他類型宣告,如 mixin)的類型參數,其整體類型隨著實際類型引數「共同變化」時,則稱其為協變。換句話說,如果類型引數被子類型取代,那麼整體類型也是子類型。

例如,List 類別的類型參數是協變的,因為清單類型會與其類型引數共同變化:List<int>List<Object> 的子類型,因為 intObject 的子類型。

在 Dart 中,所有類別、mixin、mixin 類別和列舉宣告的所有類型參數都是協變的。

但是,函式類型則不同:函式類型在其傳回類型中是協變的,但在其參數類型中則是相反的(稱為逆變)。例如,類型 int Function(int) 是類型 Object Function(int) 的子類型,但它是 int Function(Object) 的超類型。

如果您考慮其可替換性,這就說得通了。如果您呼叫靜態類型為 int Function(int) 的函式,則該函式在執行階段實際上可能是 int Function(Object) 類型。根據靜態類型,您期望能夠將 int 傳遞給它。這會很好,因為函式實際上接受任何 Object,而這包括每個 int 類型的物件。同樣地,傳回的結果將是 int 類型,這也是您根據靜態類型所期望的。

因此,int Function(Object)int Function(int) 的子類型。

請注意,對於參數類型,所有內容都會顛倒。特別是,函式類型之間的這種子類型關係要求參數類型存在相反的子類型關係。例如,void Function(Object)void Function(int) 的子類型,因為 intObject 的子類型。

對於更複雜的類型(如 List<void Function(int)>),您必須考慮類型中的位置。為此,請將類型的一部分轉換為預留位置,然後考慮當將不同的類型放置在該位置時,類型會發生什麼情況。

例如,將 List<void Function(_)> 視為類型的範本,您可以在其中將不同的類型放在預留位置 _ 取代。此類型在該預留位置發生的位置中是逆變的。

以下範例透過將 Objectint 替換為 _ 來加以說明。List<void Function(Object)>List<void Function(int)> 的子類型,因為 void Function(Object)void Function(int) 的子類型,因為 voidvoid 的子類型(傳回類型),而 intObject 的子類型(參數類型,順序相反)。因此,_ 位置的類型與整體類型 List<void Function(_)> 的變化方向相反,而這種「相反方向」依定義使其成為逆變位置

協變位置的定義類似。例如,_ 在類型 List<_> 中處於協變位置,而 _ 也在類型 _ Function(int) 中處於協變位置。

還有一種稱為不變的位置,但它很少發生,因此此處省略了詳細資訊。

實際上,通常只需知道類別、mixin 等的類型引數處於協變位置,函式類型的傳回類型也是如此,但是參數類型則處於逆變位置。

相關文件和資源