Bre*_*ton 5 generics subtype dart
我从 Dart 通用函数中收到运行时错误:
Widget card = widget.cardBuilder(item);
Run Code Online (Sandbox Code Playgroud)
产生:
type '(Contact) => Widget' is not a subtype of type '(DeletableItem) => Widget'
Run Code Online (Sandbox Code Playgroud)
Contact 类定义为
class Contact
implements DeletableItem
{
Run Code Online (Sandbox Code Playgroud)
然后我有一个方法:
class UndoableListView<T extends DeletableItem>
{
List<T> children;
final Widget Function(T item) cardBuilder;
Run Code Online (Sandbox Code Playgroud)
这就是运行时错误发生的地方
Widget buildItem(BuildContext context, int index) {
T item = children[index];
Widget card = widget.cardBuilder(item); <<<<< error thrown here.
Run Code Online (Sandbox Code Playgroud)
我显然误解了泛型如何工作。
Contact 显然扩展了 DeleteableItem。
那么我做错了什么?
Contactextends DeletableItem,并且是它的子类型。
函数的参数是逆变的,所以
Widget Function(Contact)
Run Code Online (Sandbox Code Playgroud)
不是的子类型
Widget Function(DeletableItem)
Run Code Online (Sandbox Code Playgroud)
事实上,情况恰恰相反。
函数之所以如此,是因为子类型意味着可替换性。Contact您可以在任何需要a 的地方使用a DeletableItem(因为它实现了整个DeletableItem接口), 的Contact子类型也是如此DeletableItem。
您可以在任何需要Widget Function(DeletableItem)a 的地方使用 a ,因为您可以使用 any 调用该函数(并且它会按预期返回 a)。的一个子类型也是如此。Widget Function(Contact)ContactWidgetWidget Function(DeletableItem)Widget Function(Contact)
Dart 泛型是协变的,尽管它并不总是合理的。
这意味着 aUndoableListView<Contact>是 的子类型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;
Run Code Online (Sandbox Code Playgroud)
So, when you do:
UndoableListView<DeletableItem> list = UndoableListView<Contact>();
var builder = list.cardBuilder;
Run Code Online (Sandbox Code Playgroud)
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.