Dart -- 泛型(Generics)

如果您查看基本数组类型List的API文档,您将看到该类型实际上是List<E>。<...>符号标记列表为泛型(或参数化)类型——具有形式类型参数的类型。根据约定,类型变量具有单字母名称,如E、T、S、K和V。

1. 为什么使用泛型

泛型通常是类型安全所必需的,他们对于写出严谨高质量的代码是很有用的:

  • 适当地指定泛型类型可以生成更好的代码。
  • 您可以使用泛型来减少代码重复。

如果您想要一个列表只包含字符串,您可以将它声明为list (读作“String of 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是替代类型。它是一个占位符,您可以将其视为开发人员稍后将定义的类型。

2. 使用集合字面量

List和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'
};

3. 构造函数的参数化类型

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

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);

下面的代码创建了一个具有整数键和视图类型值的map映射:

var views = Map<int, View>();

4. 泛型集合及其包含的类型

Dart通用类型被具体化,这意味着它们在运行时携带它们的类型信息。例如,您可以测试集合的类型:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

注意:相反,Java中的泛型使用擦除,这意味着泛型类型参数在运行时被删除。在Java中,您可以测试一个对象是否是一个列表,但不能测试它是否是List<String>。

5. 限制参数化类型

在实现泛型类型时,您可能希望限制其参数的类型。你可以使用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>'

指定任何非somebaseclass类型都会导致错误:

var foo = Foo<Object>();

6. 使用泛型方法

最初,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;
}

在这里,first上的泛型参数(<T>)允许你在很多地方使用类型参数T:

  • 在函数的返回中返回类型(T)
  • 在参数的类型中使用(List<T>)
  • 在局部变量的类型中(T tmp)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • object 变量可指向任何类的实例,这让你能够创建可对任何数据类型进程处理的类。然而,这种方法存在几个严重的问题...
    CarlDonitz阅读 4,440评论 0 5
  • 开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List作为形式参数,那么如果尝试...
    时待吾阅读 4,685评论 0 3
  • “泛型”这个术语的意思是:"适用于许多许多的类型”。如何做到这一点呢,正是通过解耦类或方法与所使用的类型之间的约束...
    王侦阅读 4,973评论 0 0
  • 泛型 泛型(Generic Type)简介 通常情况的类和函数,我们只需要使用具体的类型即可:要么是基本类型,要么...
    Tenderness4阅读 5,275评论 4 2
  • 宫寒滴表现形式: 发胖:宫寒的姐妹一般都会略微发胖,尤其是天生体质较寒的女性,非常容易四肢冰冷,为了维护自身的生理...
    苏苏_978b阅读 1,625评论 0 0