'Dart Generics - type is not a subtype

I'm getting a runtime error from a Dart generic function:

 Widget card = widget.cardBuilder(item);

Which generates:

 type '(Contact) => Widget' is not a subtype of type '(DeletableItem) => Widget'

The Contact class is defined as

class Contact
implements DeletableItem
{

I then have a method:

 class UndoableListView<T extends DeletableItem>
 {
      List<T> children;
      final Widget Function(T item) cardBuilder;

And this is where the runtime error occurs

 Widget buildItem(BuildContext context, int index) {
 T item = children[index];
 Widget card = widget.cardBuilder(item);   <<<<< error thrown here.

I'm clearly mis-understanding something with how generics work.

Contact clearly extends DeleteableItem.

So what have I done wrong?



Solution 1:[1]

Contact extends DeletableItem, and is a subtype of it.

Functions are contravariant in their parameters, so

Widget Function(Contact)

is not a subtype of

Widget Function(DeletableItem)

In fact, it's the other way around.

Functions act that way because subtyping means substitutability. You can use a Contact anywhere a DeletableItem is needed (because it implements the entire DeletableItem interface), so Contact is a subtype of DeletableItem.

You can use a Widget Function(DeletableItem) anywhere a Widget Function(Contact) is needed, because you can call that function with any Contact (and it returns a Widget as expected). So Widget Function(DeletableItem) is a subtype of Widget Function(Contact).

Dart generics are covariant, even though it's not always sound.

That means that a UndoableListView<Contact> is a subtype of UndoableListView<DeletableItem>.

The problem comes up because the cardBuilder field uses the class type parameter contravariantly in the function type,

      final Widget Function(T item) cardBuilder;

So, when you do:

UndoableListView<DeletableItem> list = UndoableListView<Contact>();
var builder = list.cardBuilder;

you get an error. The list.cardBuilder expression has a static type of Widget Function(DeletableItem), but a run-time type of Widget Function(Contact), which is not a subtype of the static type. That's a type soundness problem, one the compiler is aware of, and it inserts a type check to ensure that the builder variable won't end up with an invalidly typed value.

And that check throws.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 lrn