随着完全类型安全的引入,Dart 2.9在默认情况下使编写更安全的代码变得更容易。
作为其中的一部分,隐式下转换不再被允许。
什么是隐式下转换?
考虑一下这段代码。
String s = 'hello world';
Object o = s; // upcast, allowed
你总是可以把一个String 的值赋给一个Object 的变量,因为Object 是Dart中所有类型的基类。
然而,到现在为止,你也可以这样做。
Object x = 42;
int i = x; // valid, x *holds* an int value
String s = x; // valid, but shouldn't be allowed
上面的赋值被称为隐式下转换,在Dart 2.8及以下版本中有效。
虽然你不会故意写这样的代码,但隐式下转换可以在你的代码中不被察觉。
考虑一下这个例子。
List<int> values = [1, 2, 3];
List<int> doubled = values.map((v) => v * 2);
print(doubled);
// TypeError: Instance of 'MappedListIterable<int, int>': type 'MappedListIterable<int, int>' is not a subtype of type 'List<int>'
看起来这段代码应该是有效的,但它却抛出了一个运行时异常。
这是因为map 是Iterable 类的一个方法,而 是List 的一个祖先类。
更确切地说,表达式values.map((v) => v * 2) 的类型是MappedListIterable<int, int> 。这可以被分配给一个类型为List<int> 的变量,因为隐式下转换是允许的。
但它不应该这样做!为了解决这个运行时异常,我们必须写。
List<int> doubled = values.map((v) => v * 2).toList();
很容易忘记.toList() - 我已经做了很多次了!
Dart 2.9中的隐式下划线
幸运的是,在Dart 2.9中不再允许隐式降调。
List<int> values = [1, 2, 3];
List<int> doubled = values.map((v) => v * 2);
// A value of type 'Iterable<int*>*' can't be assigned to a variable of type 'List<int>'
这段代码会产生一个编译错误。第一个例子也是不允许的。
Object x = 42;
int i = x; // A value of type 'Object' can't be assigned to a variable of type 'int'
String s = x; // A value of type 'Object' can't be assigned to a variable of type 'String'
这样就好多了。
在analysis_options.yaml中禁用隐式下转换法
截止到2020年6月,Dart 2.9还没有进入稳定通道。
如果你是在Dart 2.8或以下版本,你可以在你的analysis_options.yaml 文件中禁用一些旧的 "动态 "规则。
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
这将迫使你使用显式下转换。
Object x = 42;
int i = x as int; // compiles, legit
String s = x as String; // compiles, will throw runtime exception
有了明确的下转换,你就不太可能写出错误的代码。
同样地,前面的例子中的map 和toList() 也会产生一个编译时错误。
List<int> values = [1, 2, 3];
List<int> doubled = values.map((v) => v * 2);
// A value of type 'Iterable<int>' can't be assigned to a variable of type 'List<int>'
结论
隐式下转换是Dart语言的一个不安全特性。你应该在analysis_options.yaml 中禁用它们。而在Dart 2.9中,它们将永远消失。😎
编码愉快!