Dart 的类型系统
Dart 语法跟 Java 比较类似,类型系统也非常相近,但仍然有一些细节需要注意。
[TOC]
1 | var i = 10; |
以上是一段合法的 Dart 代码。你会误以为 Dart 是动态语言,实际上并非如此。Dart 是强类型的静态语言,它结合使用了静态类型检查和运行时检查。Dart 的聪明之处在于类型推断,所以上述代码中 i
的类型被推断成 int
。
一些 Tips
静态类型的大多数规则很容易理解,但仍然有些不明显的规则。
规则一
覆盖方法时,使用稳定的返回类型。(原文:Use sound return types when overriding methods.) 换句话说,子类方法的返回类型必须与父类中方法的返回类型相同或是其子类。
考虑 Animal
类中的 getter
方法:
1 | class Animal { |
HoneyBadger
继承自 Animal
,所以可以用 HoneyBadger
作为 getter
方法的返回类型,但是不能使用一个跟 Animal
不相关的类型作为 getter
方法的返回类型。
1 | class HoneyBadger extends Animal { |
规则二
第二条规则跟前一条规则类似。覆盖方法时,使用稳定的参数类型。(原文:Use sound parameter types when overriding methods)。换句话说,子类方法的返回类型必须与父类中方法的返回类型相同或是其超类。注意,这里是不是”收紧”类型,而是”放宽”类型。
以 Animal
类的 chase(Animal)
方法为例:
1 | class Animal { |
HoneyBadger
继承自 Animal
,它的 chase()
方法可以接受 Animal
、HoneyBadger
甚至是 Object
作为参数。
1 | class HoneyBadger extends Animal { |
(官网中对这条规则有解释,但似乎含糊不清。或者是我自己没有弄懂)
如果不遵守这条规则,会报错:
1 | class Animal { |
规则三
动态列表(dynamic list)可以包含不同种类的内容。但是将动态列表作为一个有类型的列表是错误的。
1 | class Animal {} |
类型推断
Dart 的类型推断可以让代码更简洁。
1 | Map<String, dynamic> arguments = {'argA': 'hello', 'argB': 42}; |
使用 var
并让 Dart 推断类型:
1 | var arguments = {'argA': 'hello', 'argB': 42}; // Map<String, Object> |
这里给出几个类型推断的示例:
1 | // Inferred as if you wrote <int>[]. |
类型替换
覆盖方法时,可能会使用新类型替换旧类型。比如在上述规则一和规则二中,分别替换方法的返回值类型和参数类型。什么时候可以用子类型或超类型替换当前类型?
从 生产者 和 消费者 角度进行思考,有助于回答这个问题。
- 消费者吸收类型,生产者产生类型 (A consumer absorbs a type and a producer generates a type)
- 可以将消费者的类型替换为超类型,将生产者的类型替换为子类型 (You can replace a consumer’s type with a supertype and a producer’s type with a subtype)
上图中 chase()
方法是消费者,所以可以将参数类型替换成超类型。get parent
是生产者,所以可以将返回类型替换成子类型。
在以下这个例子中,Cat c
是消费者,Cat()
是生产者。
1 | Cat c = Cat(); |
所以可以将消费者类型替换为超类型:
1 | Animal c = Cat(); |
所以也可以将生产者类型替换为子类型:
1 | // MaineCoon 是 Cat 的子类 |
泛型
Dart 在运行时保留了泛型的类型信息。Java 与此不同,它在运行时已擦除类型信息。
在 Java 中,你可以判断一个对象是否 List
,但无法判断一个对象是否 List<String>
。Java 中,即便 Cat
是 Animal
的子类,但 List<Cat>
并不是 List<Animal>
的子类型。所以如下 Java 代码是错误的:
1 | // 错误: 不兼容的类型: ArrayList<Cat>无法转换为List<Animal> |
但在 Dart 中,上述限制不存在。List<Cat>
是 List<Animal>
的子类型。List
之间甚至也可以有下图中这样的子类关系。
在 Dart 中,如下代码可以正常工作:
1 | // ok |