1. 泛型概述
泛型(Generics)允許我們在類、方法和接口中使用占位符來代表類型,使代碼在不同類型間具有更好的通用性和重用性。Dart 使用 <...> 語法標記泛型類型。
2. 泛型類型的命名
在 Dart 中,泛型類型參數通常使用單個字母來命名,例如 E(元素)、T(類型)、S(類型)、K(鍵)、V(值)等。
3. 為什么使用泛型?
3.1 類型安全
泛型提供類型安全,通過在編譯時強制類型檢查,避免了運行時的類型錯誤。例如,如果我們打算創建一個只包含字符串的列表,可以聲明為 List<String>,這樣就可以在編譯時檢測到向列表中添加非字符串的錯誤。
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // 錯誤:類型不匹配
3.2 減少代碼重復
泛型允許在多個類型之間共享同一個接口和實現,從而減少代碼重復。例如,我們可以使用泛型創建一個通用的緩存接口,而不是為每種類型單獨創建接口
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
4. 泛型的使用
4.1 泛型類
定義泛型類時,可以在類名后面加上類型參數
class Box<T> {
T? value;
Box(this.value);
void showValue() {
print(value);
}
}
void main() {
var intBox = Box<int>(123);
intBox.showValue(); // 輸出: 123
var stringBox = Box<String>("Hello");
stringBox.showValue(); // 輸出: Hello
}
4.2 泛型方法
定義泛型方法時,可以在方法名之前加上類型參數。
T getFirst<T>(List<T> items) {
return items[0];
}
void main() {
var numbers = [1, 2, 3];
var firstNumber = getFirst(numbers); // 類型推斷為 int
print(firstNumber); // 輸出: 1
var words = ["apple", "banana", "cherry"];
var firstWord = getFirst(words); // 類型推斷為 String
print(firstWord); // 輸出: apple
}
4.3 使用集合字面量
可以使用帶有類型參數的集合字面量,例如列表、集合和映射。
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'
};
4.4 構造函數中的泛型類型
使用構造函數時,可以在類名后面指定泛型類型。
var nameSet = Set<String>.from(names);
var views = Map<int, View>();
5. 泛型集合和類型信息
Dart 的泛型類型是具體化的(reified),這意味著它們在運行時攜帶類型信息。
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // 輸出: true
6. 限制參數化類型
在實現泛型類型時,可能需要限制參數類型。例如,確保類型是非空的,可以使用 extends 關鍵字。
class Foo<T extends Object> {
// 提供給 Foo 的 T 類型必須是非空類型
}
class Foo<T extends SomeBaseClass> {
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
void main() {
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
var foo = Foo();
print(foo); // 輸出: Instance of 'Foo<SomeBaseClass>'
}
7. 泛型方法
方法和函數也可以使用類型參數。
T first<T>(List<T> ts) {
T tmp = ts[0];
return tmp;
}