'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 |