为什么使用泛型?
泛型常用于需要要求类型安全的情况,但是它也会对代码运行有好处:
- 适当地指定泛型可以更好地帮助代码生成。
- 使用泛型可以减少代码重复
比如你想声明一个只能包含 String 类型的数组,你可以将该数组声明为 List<String>
(读作“字符串类型的 list”),这样的话就可以很容易避免因为在该数组放入非 String 类变量而导致的诸多问题,同时编译器以及其他阅读代码的人都可以很容易地发现并定位问题:
var names = <String>[];
names.add("jack");
names.add(42); // Error
另一个使用泛型的原因是可以减少重复代码,泛型可以让你在多个不同类型实现之间共享同一个接口声明.比如声明一个方法 返回一个String类型的数据
String getData(String data) {
return data;
}
此时getData方法只能传入String类型的参数,如果现在要返回一个int类型的数据,又要重新写一个,如下
int getData(int data) {
return data;
}
这时候可以考虑使用泛型来声明一个类,让不同类型的缓存实现该类做出不同的具体实现即可:
T getData<T>(T data) {
return data;
}
在上述代码中,T 是一个替代类型。其相当于类型占位符,在开发者调用该接口的时候会指定具体类型。
官网的例子是指定泛型类,具体可以查看Language tour | Dart
//让不同类型的缓存实现该类做出不同的具体实现即可:
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
具体实现
class FileCache<T> extends Cache<T> {
@override
T getByKey(String key) {
// TODO: implement getByKey
throw UnimplementedError();
}
@override
void setByKey(String key, T value) {
print("写入缓存成功key=$key,value=$value");
}
}
main() {
var flieCache = FileCache<String>();//限定指定类型为String
//此时value传入int类型会提示错误。
flieCache.setByKey("key",
123); //The argument type 'int' can't be assigned to the parameter type 'String'
flieCache.setByKey("key", "value");
}
比如现在需要创建个泛型类,需求是传入任意类型的数据添加到List中
class MyList<T> {
List list = <T>[]; //创建一个空的数组
void add(T value) {
list.add(value); //往数据里添加数据
}
void getList() {
print(list);
}
}
未指定具体类型的,可以传入任意类型,如下
main() {
var mylist = MyList();
mylist.add("jack");
mylist.add(1);
mylist.getList();
}
输出打印:[jack, 1]
指定类型,比如我要指定集合中只能添加String类型的数据,可以这么写
main() {
var mylist = MyList<String>();//指定具体的类型,传入其他类型会报错
mylist.add("jack");
mylist.add(1);// 报错:The argument type 'int' can't be assigned to the parameter type 'String'
mylist.getList();
}
限制参数化类型
有时使用泛型的时候,你可能会想限制可作为参数的泛型范围,也就是参数必须是指定类型的子类,这时候可以使用 extends
关键字。
一种常见的非空类型处理方式,是将子类限制继承 Object
(而不是默认的 Object?
)。
class Foo<T extends Object> {
// Any type provided to Foo for T must be non-nullable.
}
除了Object之外,还可以对其他类型使用extends。下面是一个扩展SomeBaseClass的例子,这样SomeBaseClass的成员就可以在T类型的对象上被调用:
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>();