如果你查阅List类的API文档,你会看见类型的写法是List<E>
这里的E就是范型,它指代这个类型的实例是某种类型相关的。
为什么使用范型?
范型通常用来保证类型安全,并且他还有更多的好处是你的代码运行:
- 正确指定泛型类型会产生更好的代码。
- 您可以使用泛型来减少代码重复。
使用范型可以让工具检测出更多书写错误:
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);
}
你会发现你经常能用到字符串缓存,或者Int缓存,所以你又照着原有代码定义了个StringCache或者IntCache之类的:
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只是一个占位符,当你真正声明Cache对象的时候需要指明T所指代的类型。
使用集合字面量
集合字面量期望你添加明确的范型:
var names = <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 names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);
或者非命名构造函数:
var views = Map<int, View>();
范型集合和它们包含的类型
Dart中的范型是具体化的,这意味着你可以在运行时获取范型的类型,例如你可以测试集合的类型:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
注意这里和Java完全相反,Java的范型是可擦除的,你可以判断这个类型是否是
List
,而不能判断这个类型是否是List<String>
范型限制
和Java类似,限制范型的范围的方式可以使用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>();
这时你可以指定没有范型的Foo
,它的范型默认是SomeBaseClass
:
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
你不可以指定非SomeBaseClass
的子类,下面的代码不能通过编译:
var foo = Foo<Object>();
使用范型方法
起初,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;
}
类似Java的范型方法,这里定义T时需要在方法名之后定义一个新的范型变量。它有三种用途:
- 当作方法返回值
- 当作参数
- 在方法内部使用
更多信息参考使用范型方法
网友评论