本系列教程大多翻译自 Dart 官网,因本人英语水平有限,可能有些地方翻译不准确,还请各位大佬斧正。如需转载请标明出处,谢绝 CSDN 爬虫党。
Generics 泛型
如果你在 API 文档中查看基本数组类型 List,你会发现其实是 List<E>
。
<...>
记号标识 List 是一个泛型(或参数化类型),一种有正式类型的参数。有种约定俗成的规则,大多数类型变量都有个单字母的名称,比如:E,T,S,K 和 V。
为什么使用泛型?(Why use generics?)
类型安全通常需要泛型,但也有许多好处:
- 正确的指定泛型类型会让你产出更优秀的代码;
- 你可以使用泛型来减少代码重复(冗余);
如果你只在数组中插入字符串,你可以声明为 List<String>
(读作“字符串数组”)。这样的话,你的同事、编辑器工具就可以判断非字符串变量插入数组的错误:
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 就是备用类型。它起到占位符的作用,可以视为一个稍后才定义的类型。
使用集合字面量(Using collection literals)
List、Set、Map 的字面量可以被参数化,就像之前看到的那样,你可以在括号前面添加 <type>
(List、Set) 或 <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'
};
对构造函数使用参数化类型(Using parameterized types with constructors)
要在使用构造函数时指定一个或多个类型,只需在类名后面添加尖括号(<...>
),并将类型写在尖括号里面:
var nameSet = Set<String>.from(names);
var views = Map<int, View>();
泛型集合及其包含的类型(Generic collections and the types they contain)
Dart 的泛型是被重新定义(reified)了的(相对于其他语言的泛型),也就是在运行时携带类型中包含的信息。例如,你可以验证集合的类型:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
Note:与 Java 相比,泛型使用的是可擦除的(erasure),意味着泛型参数在运行时会被移除。在 Java 中,你可以测试一个对象是否是
List
,但你无法测试是否是List<String>
约束参数化类型(Restricting the parameterized type)
在实现泛型类型时,你可能想要约束参数的类型,使用 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>'
指定任何一个非基本类会报错:
var foo = Foo<Object>(); // error !!!
使用泛型方法(Using generic methods)
起初,Dart 的泛型只支持 class。一个新的语法,叫做泛型方法,允许在方法和函数中使用类型参数:
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
);
更多关于泛型的资料,请移步 Using Generic Methods
系列文章:
Dart 简明教程 - 01 - Concepts & Variables
Dart 简明教程 - 02 - Functions
Dart 简明教程 - 03 - Operators
Dart 简明教程 - 04 - Control flow statements
Dart 简明教程 - 05 - Exceptions
Dart 简明教程 - 06 - Classes
Dart 简明教程 - 07 - Generics
Dart 简明教程 - 08 - Libraries and visibility
Dart 简明教程 - 09 - Asynchrony support
Dart 简明教程 - 10 - Generators & Isolates & Typedefs & Metadata...