Reputation: 4554
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?
Upvotes: 10
Views: 1550
Reputation: 71653
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.
Upvotes: 1