如果在API文档上查看基本数组类型List,你会发现该类型实际上是List<E>
,其中<...>注解标记这次集合是一个泛型(参数化的)类 -->一种含有正规类型参数的类型。按照惯例,类型变量通常为单字符名称,例如E,T,S,K,以及V。
为何要使用泛型?
类型安全通常需要泛型,但是它们比仅仅允许代码运行有更多的好处:
- 正确指定泛型类型会生成更好的代码。
- 你可以使用泛型来减少代码重复。
如果你希望列表仅包含字符串,则可以将其声明为List<String>
(将其称为“字符串列表”)。这样,你、你的同事和你的工具就可以发现给列表分配一个非字符串是错误的。这里有一个例子:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // 错误
使用泛型的另一个原因是减少代码重复。泛型允许你在许多类型之间共享单个接口和实现,同时仍然利用静态分析。例如,假设你创建了一个用于缓存对象的接口:
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是代理类型。这是一个占位符,你可以将其视为稍后将定义的类型。
使用集合常量
Lis常量以及map常量都能被参数化。参数常量就像你已经见过的常量那样,除非你在左方括号之前添加<type>
(对于List)或者<keyType,valuetype>
(对于map)。以下是使用类型化常量的示例:
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);
接下来的代码创建一个map,key为Interger类型,value为View类型
var views = Map<int, View>();
泛型集合及其包含的类型
dart泛型类型被具体化,这意味着它们在运行时携带它们的类型信息。例如,你可以测试集合的类型:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
注:相反,Java中的泛型使用擦除,这意味着在运行时移除泛型类型参数。在Java中,你可以测试对象是否是列表,但不能测试它是否是
List<String>
。
限制参数化类型
实现泛型类型时,你可能希望限制其参数的类型。你可以使用extens来执行此操作。
class Foo<T extends SomeBaseClass> {
// 实现如下...
String toString() => "Instance of 'Foo<$T>'";
}
//继承
class Extender extends SomeBaseClass {...}
可以使用SomeBaseClass或其任何子类作为泛型参数:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
也可以不指定泛型参数:
var foo = Foo();
print(foo); // Foo<SomeBaseClass>的对象
指定任何非SomeBaseClass类型都会导致错误:
var foo = Foo<Object>();
使用泛型方法
最初,Dart的一般支持仅限于类。一种称为泛型方法的较新语法允许对方法和函数进行类型参数:
T first<T>(List<T> ts) {
// 做一些初始化工作或者错误检查,然后...
T tmp = ts[0];
// 做一些额外的检查或者处理...
return tmp;
}
这里,first
(<T
>)上的泛型类型参数允许你在几个地方使用类型参数T
:
- 在方法的返回类型(
T
) - 作为参数的类型(
List<T>
) - 作为本地变量的类型(T tmp)
PS:本文
整理
自官方文档,若有发现问题请致邮 caoyanglee92@gmail.com
网友评论