泛型(Generics)
如果你在API文档寻找基本数组类型或者 List 类型,你将会看到该类型实际上为List<E>
,其中<...>
标记表示此表为一个泛型类型(或为参数化结构)—— 一种含有正规类型参数的类型。按照惯例,类型变量通常为单字符名称,例如E,T,S,K,以及V。
为何要使用泛型?
泛型经常是被要求类型安全的,而且还有很多优势:
- 合理的指定泛型类型能够更好的生成代码
- 使用泛型减少重复代码
如果你打算使用一个仅仅包含字符串的List
,你可以声明它为List<String>
,通过这种方式,你的程序员同事,以及你的工具(比如Dart编辑器和调试模式下的Dart虚拟机)能检测到将一个非字符串的变量分配到List中很可能是错误的,这里给出一个样例:
var names = List<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
是一个替代类型。这是一个类型占位符, 在开发者调用该接口的时候会指定具体类型。
使用集合字面量
List
,set
以及map
都能被参数化,参数字面量就像你已经见过的常量那样,除非你在左方括号之前添加<type>
(对于List)或者<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 = new Map<int,view>();
泛型集合及其包含的类型
Dart
泛型类型是被具体化的,意思就是它们在整个运行时间中都携带着类型信息。举个例子,你可以测试一个集合中的类型甚至是在生产模式中:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
限制泛型类型
当使用泛型类型的时候,你可能想限制泛型的具体类型。 使用extends
可以实现这个功能:
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>(); // error
使用泛型函数
一开始,泛型只能在Dart
类中使用。 新的语法也支持在函数和方法上使用泛型了
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)
网友评论