简述:
下面开始Dart语法篇的第七篇类型系统和泛型,上一篇我们用了一篇Dart中可空和非空类型译文做了铺垫。实际上,Dart中的类型系统是不够严格,这当然和它的历史原因有关。在dart最开始诞生之初,它的定位是一门像javascript一样的动态语言,动态语言的类型系统是比较松散的,所以在Dart类型也是可选的。然后动态语言类型系统松散对开发者并不是一件好事,程序逻辑一旦复杂,松散的类型可能就变得混乱,分析起来非常痛苦,但是有静态类型检查可以在编译的时候就快速定位问题所在。
其实,dart类型系统不够严格,这一点不仅仅体现在可选类型上和还没有划分可空与非空类型上,甚至还体现dart中的泛型类型安全上,这一点我会通过对比Kotlin和Dart中泛型实现。你会发现Dart和Kotlin泛型安全完全走不是一个路子,而且dart泛型安全是不可靠的,但是也会发现dart2.0之后对这块做很大的改进。
一、可选类型
在Dart中的类型实际上是可选的,也就是在Dart中函数类型,参数类型,变量类型是可以直接省略的。
sum(a, b, c, d) {//函数参数类型和返回值类型可以省略
return a + b + c + d;
}
main() {
print('${sum(10, 12, 14, 12)}');//正常运行
}
上述的sum
函数既没有返回值类型也没有参数类型,可能有的人会疑惑如果sum
函数最后一个形参传入一个String
类型会是怎么样。
答案是: 静态类型检查分析正常但是编译运行异常。
sum(a, b, c, d) {
return a + b + c + d;
}
main() {
print('${sum(10, 12, 14, "12312")}');//静态检查类型检查正常,运行异常
}
//运行结果
Unhandled exception:
type 'String' is not a subtype of type 'num' of 'other' //请先记住这个子类型不匹配异常问题,因为在后面会详细分析子类型的含义,而且Dart、Flutter开发中会经常看到这个异常。
Process finished with exit code 255
虽然,可选类型从一方面使得整个代码变得简洁以及具有动态性,但是从另一方面它会使得静态检查类型难以分析。但是这也使得dart中失去了基于类型函数重载特性。我们都知道函数重载是静态语言中比较常见的语法特性,可是在dart中是不支持的。比如在其他语言我们一般使用构造器重载解决多种方式构造对象的场景,但是dart不支持构造器重载,所以为了解决这个问题,Dart推出了命名构造器的概念。那可选类型语法特性为什么会和函数重载特性冲突呢?
我们可以使用反证法,假设dart支持函数重载,那么可能就会有以下这段代码:
class IllegalCode {
overloaded(num data) {
}
overloaded(List data){//假设支持函数重载,实际上这是非法的
}
}
main() {
var data1 = 100;
var data2 = ["100"];
//由于dart中的类型是可选的,以下函数调用,根本就无法分辨下面代码实际上调用哪个overloaded函数。
overloaded(data1);
overloaded(data2);
}
个人一些想法,如果仅从可选类型角度去考虑的话,实际上dart现在是可以支持基于类型的函数重载的,因为Dart有类型推导功能。如果dart能够推导出上述data1和data2类型,那么就可以根据推导出的类型去匹配重载的函数。Kotlin就是这样做的,以Kotlin为例:
fun overloaded(data: Int) {
//....
}
fun overloaded(data: List<String>) {
//....
}
fun main(args: Array<String>) {
val data1 = 100 //这里Kotlin也是采用类型推导为Int
val data2 = listOf("100")//这里Kotlin也是采用类型推导为List<String>
//所以以下重载函数的调用在Kotlin中是合理的
overloaded(data1)
overloaded(data2)
}
实际上,Dart官方在Github提到过Dart迁移到新的类型系统中,Dart是有能力支持函数重载的 。具体可以参考这个dartlang的issue: