詞彙表
以下是在 Dart 文件中使用的術語定義。
輔助
一種自動化的本地程式碼編輯,旨在對程式碼進行常見的改進。
輔助是一種自動化的本地程式碼編輯,旨在對程式碼進行常見的改進。輔助的範例包括將 switch
陳述式轉換為 switch
運算式、反轉 if
陳述式中的 then
和 else
區塊,以及將小工具插入小工具結構中。
相關文件與資源
常數上下文
程式碼區域,其中 const 關鍵字是隱含的,且該區域內的所有內容都必須是常數。
常數上下文是指程式碼區域,其中不需要包含 const
關鍵字,因為它隱含在該區域中的所有內容都必須是常數的事實中。以下位置是常數上下文
以
const
關鍵字為前綴的清單、Map 或 Set 字面值內的所有內容。例如dartvar l = const [/*constant context*/];
常數建構子調用內的引數。例如
dartvar p = const Point(/*constant context*/);
以
const
關鍵字為前綴的變數的初始化器。例如dartconst v = /*constant context*/;
註解。
case
子句中的運算式。例如dartvoid f(int e) { switch (e) { case /*constant context*/: break; } }
明確賦值
判斷變數在使用前是否已被明確賦值的過程。
明確賦值分析是判斷程式碼中每個點的每個局部變數,以下哪個為真的過程
- 變數已被明確賦值(明確賦值)。
- 變數已被明確未賦值(明確未賦值)。
- 變數可能已賦值,也可能未賦值,具體取決於到達該點所採取的執行路徑。
明確賦值分析有助於找出程式碼中的問題,例如可能未賦值的變數被參考的位置,或只能賦值一次的變數在可能已經賦值後又被賦值的位置。
例如,在以下程式碼中,變數 s
在作為引數傳遞給 print
時明確未賦值
void f() {
String s;
print(s);
}
但在以下程式碼中,變數 s
已明確賦值
void f(String name) {
String s = 'Hello $name!';
print(s);
}
當存在多個可能的執行路徑時,明確賦值分析甚至可以判斷變數是否已明確賦值(或未賦值)。在以下程式碼中,如果執行通過 if
陳述式的 true 或 false 分支,則會調用 print
函式,但由於無論採用哪個分支都已賦值 s
,因此在將 s
傳遞給 print
之前已明確賦值
void f(String name, bool casual) {
String s;
if (casual) {
s = 'Hi $name!';
} else {
s = 'Hello $name!';
}
print(s);
}
在流程分析中,if
陳述式的結尾被稱為接合點—兩個或多個執行路徑在此處合併在一起的地方。在有接合點的地方,分析表示如果變數在所有合併的路徑上都已明確賦值,則該變數已明確賦值;如果變數在所有路徑上都已明確未賦值,則該變數已明確未賦值。
有時變數在一個路徑上被賦值,但在另一個路徑上未被賦值,在這種情況下,變數可能已賦值,也可能未賦值。在以下範例中,if
陳述式的 true 分支可能被執行,也可能不被執行,因此變數可能已賦值,也可能未賦值
void f(String name, bool casual) {
String s;
if (casual) {
s = 'Hi $name!';
}
print(s);
}
如果存在未將值賦予 s
的 false 分支,情況也是如此。
迴圈的分析稍微複雜一些,但它遵循相同的基本原理。例如,while
迴圈中的條件始終會被執行,但主體可能被執行,也可能不被執行。因此,就像 if
陳述式一樣,在 while
陳述式的結尾,在條件為 true
的路徑與條件為 false
的路徑之間存在一個接合點。
不可反駁的模式
始終匹配的模式。
Mixin 應用
將 mixin 應用於類別時建立的類別。
Mixin 應用是將 mixin 應用於類別時建立的類別。例如,考慮以下宣告
class A {}
mixin M {}
class B extends A with M {}
類別 B
是將 M
的 mixin 應用於 A
的 mixin 應用的子類別,有時命名為 A+M
。類別 A+M
是 A
的子類別,並且具有從 M
複製的成員。
您可以透過將 mixin 應用定義為以下形式來為 mixin 應用指定實際名稱
class A {}
mixin M {}
class A_M = A with M;
給定 A_M
的此宣告,以下 B
的宣告與原始範例中 B
的宣告等效
class B extends A_M {}
相關文件與資源
覆寫推斷
方法宣告中遺失的類型如何推斷。
覆寫推斷是根據它覆寫的方法或方法中的相應類型,來推斷方法宣告中任何遺失類型的過程。
如果候選方法(遺失類型資訊的方法)覆寫單一繼承方法,則會推斷覆寫方法中的相應類型。例如,考慮以下程式碼
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 用於推斷遺失的類型。例如,考慮以下程式碼
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
中的 m
和 B
中的 m
,因此編譯器需要選擇其中一個來推斷遺失的類型。但是,由於 A
中 m
的函式類型 (int Function(num)
) 是 B
中 m
的函式類型 (num Function(int)
) 的父類型,因此 A
中的函式用於推斷遺失的類型。結果與將 C
中的方法宣告為 int m(num n) => 1;
相同。
如果沒有任何覆寫方法的函式類型是所有其他覆寫方法的父類型,則會發生錯誤。
相關文件與資源
Part 檔案
包含 part of
指令的 Dart 原始碼檔案。
Part 檔案是包含 part of
指令的 Dart 原始碼檔案,並使用 part
指令包含在函式庫中。
可能為非空值
明確為非空值或由於是類型參數而為非空值的類型。
如果類型明確為非空值,或者如果是類型參數,則類型可能為非空值。
如果類型名稱未後跟問號 (?
),則該類型明確為非空值。請注意,有一些類型始終可為空值,例如 Null
和 dynamic
,並且只有在 FutureOr
後面沒有問號且類型引數為非空值時(例如 FutureOr<String>
),FutureOr
才為非空值。
類型參數可能為非空值,因為實際執行階段類型(指定為類型引數的類型)可能為非空值。例如,給定 class C<T> {}
的宣告,類型 C
可以與非空值類型引數一起使用,如 C<int>
中所示。
相關文件與資源
公開函式庫
位於套件的 lib
目錄中,但不位於 lib/src
目錄內的函式庫。
公開函式庫是位於套件的 lib
目錄中,但不位於 lib/src
目錄內的函式庫。
重構
一種程式碼編輯,旨在進行非本地或需要使用者互動的修改。
重構是一種程式碼編輯,旨在進行非本地或需要使用者互動的修改。重構的範例包括重新命名、移除或提取程式碼。
相關文件與資源
子類別
繼承另一個類別實作的類別。
子類別是使用 extends
關鍵字或透過 mixin 應用繼承另一個類別實作的類別。
// 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
隱含地定義了相關類型 A
,類別 A
的實例存在於其中。因此,class A extends B
不僅宣告類別 A
是 B
的子類別,而且還建立了類型 A
是類型 B
的子類型。
子類別關係是子類型關係的子集。當文件說「S
必須是 T
的子類型」時,S
是 T
的子類別是可以的。但是,反之則不然:並非所有子類型都是子類別。
子類型
可以在預期其父類型值的位置使用的類型。
子類型關係是指在預期另一種類型(父類型)的值時,可以使用某種類型的值進行替換的情況。例如,如果 S
是 T
的子類型,則可以在預期類型 T
的值的位置替換類型 S
的值。
子類型支援其父類型的所有操作(以及可能的一些額外操作)。實際上,這表示您可以將子類型的值賦值給任何預期父類型的位置,並且父類型的所有方法在子類型上都可用。
至少在靜態上是如此。特定的 API 可能不允許在執行階段進行替換,具體取決於其操作。
某些子類型關係基於類型的結構,例如可空值類型(例如,int
是 int?
的子類型)和函式類型(例如,String Function()
是 void Function()
的子類型)。
// 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 {}
相關文件與資源
變異數與變異數位置
變更類型的類型引數如何影響原始類型與結果類型之間的關係。
在 Dart 中,變更類型宣告(例如類別)或函式回傳類型的類型引數,會以相同方向(協變)變更整體類型關係。
但是,變更函式參數類型的類型,會以相反方向(反變)變更整體類型關係。
當類型整體「協變」於實際類型引數時,類別(或其他類型宣告,例如 mixin)的類型參數被稱為協變。換句話說,如果類型引數被子類型取代,則類型整體也是子類型。
例如,類別 List
的類型參數是協變的,因為清單類型與其類型引數協變:List<int>
是 List<Object>
的子類型,因為 int
是 Object
的子類型。
在 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)
的子類型,因為 int
是 Object
的子類型。
對於更複雜的類型(如 List<void Function(int)>
),您必須考慮類型中的位置。為了完成此操作,請將類型的一部分變成佔位符,然後考慮將不同類型放置在該位置時類型會發生什麼變化。
例如,考慮 List<void Function(_)>
作為一種範本類型,您可以在其中將不同類型放在佔位符 _
的位置。此類型在佔位符發生的位置是反變的。
以下透過將 Object
和 int
替換為 _
來說明這一點。List<void Function(Object)>
是 List<void Function(int)>
的子類型,因為 void Function(Object)
是 void Function(int)
的子類型,因為 void
是 void
的子類型(回傳類型),而 int
是 Object
的子類型(參數類型,順序相反)。因此,_
位置的類型與類型 List<void Function(_)>
整體以相反的方向變化,而這種「相反方向」依定義使其成為反變位置。
協變位置的定義類似。例如,_
在類型 List<_>
中處於協變位置,_
在類型 _ Function(int)
中也處於協變位置。
還有另一種位置稱為不變,但它很少發生,因此此處省略詳細資訊。
實際上,通常足以知道類別、mixin 等的類型引數處於協變位置,函式類型的回傳類型也是如此,但參數類型處於反變位置。
除非另有說明,否則本網站上的文件反映了 Dart 3.7.1。頁面上次更新於 2025-03-07。 檢視原始碼 或回報問題。