Dart之旅08: 范型

如果你查阅List类的API文档,你会看见类型的写法是List<E>这里的E就是范型,它指代这个类型的实例是某种类型相关的。

为什么使用范型?

范型通常用来保证类型安全,并且他还有更多的好处是你的代码运行:

  • 正确指定泛型类型会产生更好的代码。
  • 您可以使用泛型来减少代码重复。

使用范型可以让工具检测出更多书写错误:

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);
}

你会发现你经常能用到字符串缓存,或者Int缓存,所以你又照着原有代码定义了个StringCache或者IntCache之类的:

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只是一个占位符,当你真正声明Cache对象的时候需要指明T所指代的类型。

使用集合字面量

集合字面量期望你添加明确的范型:

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);

或者非命名构造函数:

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

范型集合和它们包含的类型

Dart中的范型是具体化的,这意味着你可以在运行时获取范型的类型,例如你可以测试集合的类型:

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

注意这里和Java完全相反,Java的范型是可擦除的,你可以判断这个类型是否是List,而不能判断这个类型是否是List<String>

范型限制

和Java类似,限制范型的范围的方式可以使用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>();

这时你可以指定没有范型的Foo,它的范型默认是SomeBaseClass:

var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'

你不可以指定非SomeBaseClass的子类,下面的代码不能通过编译:

var foo = Foo<Object>();

使用范型方法

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

类似Java的范型方法,这里定义T时需要在方法名之后定义一个新的范型变量。它有三种用途:

  • 当作方法返回值
  • 当作参数
  • 在方法内部使用

更多信息参考使用范型方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,170评论 1 32
  • //Clojure入门教程: Clojure – Functional Programming for the J...
    葡萄喃喃呓语阅读 3,853评论 0 7
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,851评论 0 4
  • 回答这个问题,突然语塞:2016年,好像没有读书啊? 其实是想不起来了。 为了工作,读了很多相关领域的书:化学专业...
    杰出小卡阅读 193评论 0 0
  • 故事该怎么算起? 是从盛夏,还是从暖冬? 毕竟他们一起走过了好多好多的春秋冬夏。 岁月太长,所幸还记得—— 透过玻...
    yifans夏术阅读 240评论 0 0