使用 Container 在 Dart 中类型安全

Type safety in Dart using Container

我在 dart 中发现了一些奇怪的东西。如果有一个包含基础 class 实例的列表(在本例中为 Super),则可以使用继承实例的列表来设置该列表。看来这会在运行时更改列表类型。

这是预期的行为还是 Dart 中的错误?

abstract class Super {}

class A extends Super {}

class B extends Super {}

class Container {
List<Super> mylist = [];
Container(this.mylist);
}

void main() {
// 1. dont't works
final container = Container(<A>[A(), A()]);
// 2. works
final container = Container([A(), A()]);
print(container.mylist.runtimeType);

container.mylist.add(B());
print(container.mylist);
}

如果在上面的代码中使用案例 1,我会得到以下错误:

JSArray<A>
Uncaught Error: TypeError: Instance of 'B': type 'B' is not a subtype of type 'A'

错误出现在我尝试添加 B 实例的行:

container.mylist.add(B());

Dart 有一个名为 type promotion, where it can promote the type of a variable, similar to type inference 的系统。

它作为 cast 工作。在第一个示例中,您已明确将列表类型提升为 A 类型,因此这没有什么奇怪的。

看看第一篇解释这个机制的文章。

当你这样做时:

final container = Container(<A>[A(), A()]);

您明确创建了一个 List<A> 对象。虽然 Container 的构造函数需要 List<Super>,但它接受 List<A> 参数,因为 Dart 认为 Generic<Derived>Generic<Base> 的子类型,如果 DerivedBase 的子类型。您稍后尝试执行 container.mylist.add(B()); 将失败,因为 container.mylist 实际上是一个 List<A>,因此不能合法地存储任何 B 元素。

当你改为:

final container = Container([A(), A()]);

然后,因为 List 字面量没有给出显式类型,所以根据 Container 的预期构造参数推断其类型为 List<Super>container.mylist.add(B()); 会成功,因为 container.mylist 实际上是一个 List<Super>,因此可以合法地存储 B 个元素。