Dart 泛型 - 类型不是子类型

8

我在Dart的一个泛型函数中遇到了运行时错误:

 Widget card = widget.cardBuilder(item);

这将生成:

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

Contact类的定义如下:

class Contact
implements DeletableItem
{

接下来,我有一个方法:

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

这就是运行时错误发生的地方。

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

我对泛型的工作方式显然存在误解。

Contact明确扩展了DeleteableItem。

那么我做错了什么?


问题不在于 Contact 没有实现 DeletableItem,而是整个函数本身的签名不同,因为 (Contact) => Widget 无法转换为 (DeletableItem) => Widget。如果没有看到更多的代码,很难提出一个修复建议。您能展示一下 buildItem 被使用的上下文吗? - spenster
这段代码是从一个相当大的应用程序中提取出来的。目的是将可删除项目列表传递给列表,并使列表回调生成卡片的构建器。我困惑的是,既然联系人是可删除类型,为什么它不能被强制转换? - Brett Sutton
有关此事有任何更新吗?我遇到了相同的错误,我不明白为什么Contract不是DeletablItem的子类型,尽管它确实是! - Théo Champion
这里并没有说Contact不是DeletableItem的子类型,而是函数类型Widget Function(Contact)不是函数类型Widget Function(DeletableItem)的子类型。函数类型在其参数类型上是逆变的(如果第一个函数的参数类型是第二个函数的参数类型的超类型,则函数类型只能是另一个函数类型的子类型)。因此,这正是预期的工作方式。 - lrn
1个回答

1

Contact继承自DeletableItem,并且是它的子类型。

函数在其参数方面是逆变的,因此

Widget Function(Contact)

不是

的子类型。

Widget Function(DeletableItem)

事实上,情况恰恰相反。
函数行为是这样的,因为子类型意味着可替换性。您可以在任何需要DeletableItem的地方使用Contact(因为它实现了整个DeletableItem接口),所以ContactDeletableItem的一个子类型。
您可以在任何需要Widget Function(Contact)的地方使用Widget Function(DeletableItem),因为您可以使用任何Contact调用该函数(并且它按预期返回Widget)。因此Widget Function(DeletableItem)Widget Function(Contact)的一个子类型。
Dart泛型是协变的,即使不总是安全的。这意味着UndoableListView<Contact>UndoableListView<DeletableItem>的一个子类型。
问题出现在cardBuilder字段中,该字段在函数类型中逆变使用类类型参数。
      final Widget Function(T item) cardBuilder;

所以,当你执行以下操作时:
UndoableListView<DeletableItem> list = UndoableListView<Contact>();
var builder = list.cardBuilder;

您遇到了一个错误。 list.cardBuilder 表达式的静态类型Widget Function(DeletableItem),但运行时类型为 Widget Function(Contact),这不是静态类型的子类型。这是类型完整性问题,编译器已经意识到并插入了类型检查,以确保 builder 变量不会得到无效类型的值。
而这个检查会抛出异常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接