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;
}