切换到声音空安全后简单赋值中的可空性不匹配

Nullability mismatch in simple assignment after switching to sound null safety

我切换到 sound null safety 并开始在一个简单的赋值中出现运行时错误,这在 sound null safety 下永远不会发生:

final widgetOnPressed = widget.onPressed;

错误:

type '(LogData) => void' is not a subtype of type '((LogData?) => void)?'

我可以为 Flutter 版本 2.12.0-4.1.pre2.13.0-0.0.pre.505 重现它。

公关:https://github.com/flutter/devtools/pull/3971

失败行:https://github.com/flutter/devtools/blob/9fc560ff2e6749459e2ca6a1dc00bf6fb16ed93b/packages/devtools_app/lib/src/shared/table.dart#L1184

要重现,请在此 PR for macos 启动 DevTools,连接到应用程序并单击选项卡 'Logging'。 DevTools 将在控制台中显示红屏和错误。

是飞镖错误还是应用程序错误?如果是app bug,如何调试?

这是您的代码中的错误。

您没有说您遇到了哪种错误 - compile-time 错误或运行时错误。我猜是运行时错误。 (好吧,你确实说过要在调试器中启动它,所以这也是一个很好的提示。)

final widgetOnPressed = widget.onPressed; 行看起来不可能失败。毕竟,局部变量的类型是从分配给它的表达式推断出来的,并且该表达式的运行时值肯定是静态类型的子类型,因为类型系统是健全的! 不是吗?不是吗?

不是,抱歉。 Dart 2 的类型系统 大部分 是合理的,空安全更是如此,但是 class 泛型 是协变的,这仍然是不合理的.很难找到其中一种情况,其中不健全表现出丑陋的头脑,但是 返回一个函数 其中 参数类型 是 class的类型变量是一个。

您的州 class 扩展 State<TableRow<T?>>,因此 widget getter returns 一个 TableRow<T?>。该类型的 onPressed 具有类型 ItemCallback<T?>?,又名 void Function(T?)?

您创建了一个 _TableRowState<LogData>,其 widget 具有 static 类型 TableRow<LogData?>,但您 不知何故 设法将它传递给 TableRow<LogData>。没关系。 Class 泛型是协变的,因此在 compile-time.

时一切显然都很好

然后你final widgetOnPressed = widget.onPressed;。 这里widgetOnPressed的静态类型是void Function(LogData?)onPressed 实际运行时类型 void Function(LogData) 因为它来自 TableRow<LogData>.

A void Function(LogData) is-not-a void Function(LogData?) 因为前者不能用在所有后者可以用的地方(特别是不能用在调用它的地方) null).

这个分配可能是不合理的,在这种情况下实际上是不合理的。编译器知道这一点并插入一个额外的检查以确保您不会为实际上无效的变量赋值。该检查会触发并抛出您看到的错误。

你如何避免这种情况?

不要在需要 TableRow<LogData?> 的地方创建 TableRow<LogData>

或将变量键入:

final ItemCallback<T>? widgetOnPressed = widget.onPressed;

T 上没有 ?)。

或重写所有内容以避免返回具有协变类型参数(来自 class)的函数出现 contra-variantly(作为参数类型)。

哪种解决方案适合您取决于您​​希望能够做什么。