在查看基本数组类型List的API文档,会看到该类型实际上是List。<...>表示法将List标记为泛型(或参数化)类型 - 具有正式类型参数的类型。按照惯例,大多数类型变量都有单字母名称,例如E,T,S,K和V.
为什么要使用泛型
类型安全通常需要泛型,除了允许让代码运行之外,还可以:正确指定泛型类型会生成更好的代码。
例如,可以使用List(字符串列表),来表示列表中只包含字符串,这样编译器就会启用类型检查,非字符串类型是无法加入该列表的。
1
2
3var names = List();
names.addAll(['a', 'b', 'c']);
// names.add(1); // 错误可以减少代码重复。
泛型允许我们在多种类型之间共享单个接口和实现,同时仍然利用静态分析。例如,创建一个用于缓存对象的接口:
1
2
3
4
5// 抽象类
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
当缓存字符串对象时,需要创建另一个版本,实际上写法相差无异,只是类型不一致。
1
2
3
4abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
此时会发现使用泛型可以省去这些麻烦,使用泛型示例:
1
2
3
4abstract class cache {
T getByKey(String key);
void setByKey(String key, T value);
}
上面示例中的T只是替身类型,在这里充当一个占位符,可视为开发人员稍后定义的类型。
使用集合文字
列表(List),集合(Set),和映射(Map)可以参数化。参数化文字对列表和集合来说在其前面加上,例如:
1
2var namesList = ['a', 'b', 'c'];
var uniqueNames = {'a', 'b', 'c'};
对映射(Map)来说,在其前面加上,例如:
1
2
3
4
5var pages = {
'index.html' : 'Homepage',
'robots.txt' : 'Hints for web robots',
'humans.txt' : 'we are people, not machines'
};
使用带有参数类型的构造函数
要在使用构造函数时指定一个或多个类型,请将类型放在类名称后面的尖括号<...>中。例如:
1
2
3
4// 集合
var nameSet = Set.from(names);
// Map
var views = Map();
通用集合及其包含的类型
Dart的泛型是具体的,也就是说他们在运行时会携带着类型信息。例如:
1
2
3
4var names = List();
names.addAll(['Seth', 'Kathy', 'Lars']);
bool b = names is List;
print('result:$b'); //true相反,Java中的泛型使用擦除,也就是说Java在运行时删除泛型类型参数。在Java中,您可以测试对象是否为List,但您无法测试它是否为一个字符串列表(List)。
限制参数类型
在使用泛型时,可以使用extends关键字来限制其参数类型:
1
2
3
4
5
6
7
8
9
10
11
12class SomeBaseClass {
}
// 泛型,限制类型
class Foo {
// Implementation goes here
String toString() => "Instace of 'Foo'";
}
// 继承
class Extender extends SomeBaseClass {
//do something
}
使用SomeBaseClass或其任何子类作为通用参数都是可以的,也可以不指定类型:
1
2
3
4
5
6var someBaseClassFoo = Foo();
// 可以使用子类
var extenderFoo = Foo();
// 也可以不指定泛型的类型
var foo = Foo();
print(foo);
使用泛型方法
Dart的泛型最初只支持对类使用。现在Dart的新语法已经支持在方法和函数上使用泛型。
1
2
3
4
5
6
7
8
9
10// 泛型方法
T first(List ts) {
// Do some initial work or error checking, then...
// List中的参数类型是泛型
T tmp = ts[0];
// Do some additional checking or processing...
//返回一个泛型的方法
return tmp;
}
上边first()的泛型类型参数,允许在以下几个地方使用类型参数T:在函数的返回类型T中。
在参数类型List中。
在局部变量的类型T tmp中。