Flutter入门——泛型

Posted by lingjye on July 14, 2019

在查看基本数组类型List的API文档,会看到该类型实际上是List。<...>表示法将List标记为泛型(或参数化)类型 - 具有正式类型参数的类型。按照惯例,大多数类型变量都有单字母名称,例如E,T,S,K和V.

为什么要使用泛型

类型安全通常需要泛型,除了允许让代码运行之外,还可以:

  • 正确指定泛型类型会生成更好的代码。

例如,可以使用List(字符串列表),来表示列表中只包含字符串,这样编译器就会启用类型检查,非字符串类型是无法加入该列表的。

1
2
3
var names = List<String>();
names.addAll(['a', 'b', 'c']);
// names.add(1); // 错误
  • 可以减少代码重复。

泛型允许我们在多种类型之间共享单个接口和实现,同时仍然利用静态分析。例如,创建一个用于缓存对象的接口:

1
2
3
4
5
// 抽象类
abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

当缓存字符串对象时,需要创建另一个版本,实际上写法相差无异,只是类型不一致。

1
2
3
4
abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}

此时会发现使用泛型可以省去这些麻烦,使用泛型示例:

1
2
3
4
abstract class cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}

上面示例中的T只是替身类型,在这里充当一个占位符,可视为开发人员稍后定义的类型。

使用集合文字

列表(List),集合(Set),和映射(Map)可以参数化。参数化文字对列表和集合来说在其前面加上,例如:

1
2
var namesList = <String>['a', 'b', 'c'];
var uniqueNames = <String>{'a', 'b', 'c'};

对映射(Map)来说,在其前面加上<keyType, valueType>,例如:

1
2
3
4
5
var pages = <String, String> {
	'index.html' : 'Homepage',
	'robots.txt' : 'Hints for web robots',
	'humans.txt' : 'we are people, not machines'
};

使用带有参数类型的构造函数

要在使用构造函数时指定一个或多个类型,请将类型放在类名称后面的尖括号<...>中。例如:

1
2
3
4
// 集合
var nameSet = Set<String>.from(names);
// Map
var views = Map<int, View>();

通用集合及其包含的类型

Dart的泛型是具体的,也就是说他们在运行时会携带着类型信息。例如:

1
2
3
4
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
bool b = names is List<String>;
print('result:$b'); //true
  • 相反,Java中的泛型使用擦除,也就是说Java在运行时删除泛型类型参数。在Java中,您可以测试对象是否为List,但您无法测试它是否为一个字符串列表(List)。

限制参数类型

在使用泛型时,可以使用extends关键字来限制其参数类型:

1
2
3
4
5
6
7
8
9
10
11
12
class SomeBaseClass {

}
// 泛型,限制类型
class Foo<T extends SomeBaseClass> {
  // Implementation goes here
  String toString() => "Instace of 'Foo<$T>'";
}
// 继承
class Extender extends SomeBaseClass {
  //do something
}

使用SomeBaseClass或其任何子类作为通用参数都是可以的,也可以不指定类型:

1
2
3
4
5
6
var someBaseClassFoo = Foo<SomeBaseClass>();
// 可以使用子类
var extenderFoo = Foo<Extender>();
// 也可以不指定泛型的类型
var foo = Foo();
print(foo);

使用泛型方法

Dart的泛型最初只支持对类使用。现在Dart的新语法已经支持在方法和函数上使用泛型。

1
2
3
4
5
6
7
8
9
10
// 泛型方法
T first<T>(List<T> ts) {
  // Do some initial work or error checking, then...
  // List中的参数类型是泛型
  T tmp = ts[0];
  // Do some additional checking or processing...
  
  //返回一个泛型的方法
  return tmp;
}

上边first()的泛型类型参数,允许在以下几个地方使用类型参数T:

  1. 在函数的返回类型T中。
  2. 在参数类型List<T>中。
  3. 在局部变量的类型T tmp中。

泛型使用说明

本文Demo