Home > OS >  Type safety in Dart using Container
Type safety in Dart using Container

Time:02-12

I found something strange in dart. If there is a list that contains instances of a base class (in this example Super), the list can be set with a list of inherited instances. It seems that this changes the list type at runtime.

Is this intended behavior or is this a bug in 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);
}

If case 1 is used in the code above I get the following error:

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

The error is at the line where I try to add an instance of B:

container.mylist.add(B());

CodePudding user response:

Dart has a system called type promotion, where it can promote the type of a variable, similar to type inference.

It works as a cast. On the first example you've explicit promoted the type of your list to be of type A, so there's nothing strange about this.

Take a look at the first article that explains this mechanism.

CodePudding user response:

When you do:

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

you explicitly create a List<A> object. Although Container's constructor expects a List<Super>, it accepts a List<A> argument because Dart considers Generic<Derived> to be a subtype of Generic<Base> if Derived is a subtype of Base. Your later attempt to do container.mylist.add(B()); will fail because container.mylist is actually a List<A> and therefore cannot legally store any B elements.

When you instead do:

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

then, because the List literal is not given an explicit type, its type is inferred to be List<Super> from Container's expected construction parameter. container.mylist.add(B()); will succeed since container.mylist is actually a List<Super> and therefore can legally store B elements.

  • Related