当试图将超类对象添加到列表时,如何克服列表子类型推断问题



在Flutter应用程序中,我有一个Column的小部件层次结构的Row。然而,我想在每个交替的小部件列中添加一个额外的SizedBox

Column getColumn(List<DAO> column, int index, BoardLayout layout) {
final key = column.map((c) => c.toString()).toList().join('_');
final items = column.map((c) => MyWidget(data: c)).toList();
if (index % 2 == 0 && layout == BoardLayout.Hexagonal)
items.add(SizedBox(height: magicNumber));
return Column(children: items);
}

这会产生"The argument type 'SizedBox' can't be assigned to the parameter type 'MyWidget'."的编译时错误。这是可以理解的,因为final items一定得到了推断的类型List<MyWidget>,然后我想添加一个SizedBox。所有这些都具有超类型Widget。如果我将其修改为:

Column getColumn(List<DAO> column, int index, BoardLayout layout) {
final key = column.map((c) => c.toString()).toList().join('_');
List<Widget> items = column.map((c) => MyWidget(data: c)).toList();
if (index % 2 == 0 && layout == BoardLayout.Hexagonal)
items.add(SizedBox(height: magicNumber));
return Column(children: items);
}

它进行编译,但随后抛出_TypeError"type 'SizedBox' is not a subtype of type 'MyWidget' of 'value'"的运行时异常。我还没有找到一种方法来强制播放列表。我尝试了List的cast<T>,并尝试了显式的(Widget)(List<Widget>)强制转换前缀,但我的尝试都还没有编译。对此有什么可能的解决方案?MyWidgetDAO的细节无关紧要,我希望列表是一个超类类型,这样我就可以向其中添加各种类型的小工具。

当您调用column.map时,Dart会根据内部函数返回的内容推断类型。在这种情况下,它返回一个MyWidget,因此即使Dart的类型系统允许您将items声明为List<Widget>,它实际上也是List<MyWidget>。(在即将推出的Dart版本中,这种模糊性被认为已经消除了。(

要解决此问题,您需要向map添加一个类型参数,以明确说明您希望它返回哪种类型的列表:

List<Widget> items = column.map<Widget>((c) => MyWidget(data: c)).toList();

另一方面,Dart中的选角是使用as关键字完成的。例如,如果你想使用铸造来修复这个问题,它看起来像这样:

List<Widget> items = column.map((c) => MyWidget(data: c) as Widget).toList();

但在我看来,map上的类型参数是更干净的方法。


这是一个问题的原因是,与大多数编程语言不同,Dart允许将超类型值分配给子类型变量。这本质上意味着,如果将超类型值分配给子类型变量,即使运行时不允许,编译也会允许它。

以以下为例:

int a = 2.5; // Compilation error: double is not int

这个代码显然会抛出一个错误,因为double不是int。但是,如果您将其更改为以下内容:

int a = 2.5 as num; // Linter warning: unnecessary cast

我们在IDE中得到的关于这段代码的唯一通知是,将double强制转换为num是不必要的,但这现在掩盖了一个相当严重的错误。和以前一样,我们试图将double值分配给int变量,但由于强制转换,编译器所看到的只是我们试图将一个num分配给一个int。在大多数强类型语言中,这仍然会导致编译错误,因为即使所有int都是num,也不是所有num都是int,因此不能保证值和变量之间的兼容性。然而,Dart决定在编译中无论如何都允许赋值,直到程序运行时才抛出类型不匹配错误。

我一辈子都想不出Dart工程师以这种方式设计语言的原因,因为我能看到的唯一实际结果是将一个容易发现的编译错误变成一个潜在的难以捉摸的运行时错误。我能想到的唯一可能的理由是,它是为了与JavaScript的互操作性(Dart最初是为JavaScript开发的(,是Dart年轻时作为弱类型语言的产物。这是一个相当大的设计缺陷,正如我之前所说,我相信Dart的未来版本会消除这个"缺陷";特征";(虽然我找不到我读过的那一页上写着这样的话,所以我希望我不是在抽烟(。

最新更新