泛型
如果您查看基本陣列類型 List
的 API 文件,您會發現該類型實際上是 List<E>
。 <...>
符號表示 List 是一個泛型(或參數化)類型,這是一種具有形式類型參數的類型。按照慣例,大多數類型變數都有單字母名稱,例如 E、T、S、K 和 V。
為什麼要使用泛型?
#泛型通常是類型安全所必需的,但它們不僅僅是讓您的程式碼運作,還具有更多優點
- 正確指定泛型類型可以產生更好的程式碼。
- 您可以使用泛型來減少程式碼重複。
如果您希望清單僅包含字串,您可以將其宣告為 List<String>
(讀作「字串清單」)。這樣一來,您、您的同事程式設計師和您的工具就可以偵測到將非字串指派給清單可能是錯誤的。這是一個範例
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
使用泛型的另一個原因是減少程式碼重複。泛型讓您可以在多種類型之間共享單一介面和實作,同時仍然利用靜態分析。例如,假設您建立一個用於快取物件的介面
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
您發現您想要這個介面的特定字串版本,因此您建立了另一個介面
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
稍後,您決定要這個介面的特定數字版本... 您明白了。
泛型類型可以讓您省去建立所有這些介面的麻煩。相反地,您可以建立一個帶有類型參數的單一介面
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
在這段程式碼中,T 是預留類型。它是一個預留位置,您可以將其視為開發人員稍後將定義的類型。
使用集合常值
#清單、集合和 Map 常值可以參數化。參數化常值就像您已經看過的常值一樣,只是您在左方括號之前添加了 <type>
(對於清單和集合)或 <keyType, valueType>
(對於 Map)。以下是使用類型常值的範例
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
使用帶有建構式的參數化類型
#若要在使用建構式時指定一或多個類型,請在類別名稱之後的角括號 (<...>
) 中放置類型。例如
var nameSet = Set<String>.from(names);
以下程式碼建立一個具有整數鍵和 View 類型值的 Map
var views = Map<int, View>();
泛型集合和它們包含的類型
#Dart 泛型類型是具體化的,這表示它們在執行時攜帶其類型資訊。例如,您可以測試集合的類型
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
限制參數化類型
#在實作泛型類型時,您可能想要限制可以作為引數提供的類型,以便引數必須是特定類型的子類型。您可以使用 extends
來執行此操作。
一個常見的用例是透過將類型設為 Object
的子類型(而不是預設的 Object?
)來確保類型不可為空值。
class Foo<T extends Object> {
// Any type provided to Foo for T must be non-nullable.
}
您可以使用 extends
和 Object
以外的其他類型。以下是擴展 SomeBaseClass
的範例,因此可以在 T 類型的物件上呼叫 SomeBaseClass
的成員
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
將 SomeBaseClass
或其任何子類型作為泛型引數是可以的
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
指定沒有泛型引數也是可以的
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
指定任何非 SomeBaseClass
類型都會產生錯誤
var foo = Foo<Object>();
使用泛型方法
#方法和函式也允許類型引數
T first<T>(List<T> ts) {
// Do some initial work or error checking, then...
T tmp = ts[0];
// Do some additional checking or processing...
return tmp;
}
這裡 first
(<T>
) 上的泛型類型參數允許您在多個位置使用類型引數 T
- 在函式的回傳類型 (
T
) 中。 - 在引數的類型 (
List<T>
) 中。 - 在區域變數的類型 (
T tmp
) 中。
除非另有說明,否則本網站上的文件反映 Dart 3.6.0。頁面最後更新於 2024-11-17。 檢視原始碼 或 回報問題。