迁移到Dart空安全后,“无法无条件调用运算符,因为接收器可以为空”错误
我正在升级一个基于 Flutter 框架的个人包。我在 Flutter Text 小部件源代码中注意到这里有一个空检查:
if (textSpan != null) {
properties.add(textSpan!.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition));
}
但是,textSpan!仍然使用!运算符。不textSpan应该在不必使用!运算符的情况下提升为不可为空的类型吗?但是,尝试删除运算符会出现以下错误:
An expression whose value can be 'null' must be null-checked before it can be dereferenced. Try checking that the value isn't 'null' before dereferencing it.An expression whose value can be 'null' must be null-checked before it can be dereferenced. Try checking that the value isn't 'null' before dereferencing it.
这是一个独立的示例:
我收到一个编译时错误:
错误:“字符串?”类型的值 无法从函数“myString”返回,因为它的返回类型为“String”。
或者,如果我尝试获取,则会_mySting.length出现以下错误:
不能无条件访问属性“length”,因为接收者可以为“null”。
我认为进行空检查会提升_myString为不可空类型。为什么不呢?
我的问题在 GitHub 上得到了解决,所以我在下面发布了一个答案。
回答
Dart 工程师 Erik Ernst在 GitHub 上说:
类型提升只适用于局部变量。... 实例变量的提升是不合理的,因为它可能会被运行计算并在每次调用时返回不同对象的 getter 覆盖。参见 dart-lang/language#1188用于讨论类似于类型提升但基于动态检查的机制,以及一些相关讨论的链接。
所以本地类型推广有效:
String myMethod(String? myString) {
if (myString == null) {
return '';
}
return myString;
}
但是实例变量不会提升。为此,您需要使用!运算符手动告诉 Dart 在这种情况下您确定实例变量不为空:
class MyClass {
String? _myString;
String myMethod() {
if (_myString == null) {
return '';
}
return _myString!;
}
}
回答
错误:
假设这是您的代码,您正在对实例变量进行空检查,但仍然看到错误:
class Foo {
int? i = 0;
double bar() {
if (i != null) return i.toDouble(); // <-- Error
return -1;
}
}
方法 'toDouble' 不能无条件调用,因为接收者可以为 'null'。
您在这样的代码中看到的错误是因为Getter 没有被提升为它们的不可为空的对应物。让我们来谈谈原因。
错误原因:
比方说,有一个类Bar扩展Foo和覆盖i变量并且不为其分配任何值(保留它null):
class Bar extends Foo {
@override
int? i;
}
所以,如果你能做到
print(Bar().d() * 2);
您会遇到运行时 null 错误,这就是禁止 getter 类型提升的原因。
解决方案:
我们需要从int?. 通常有3种方法可以做到这一点(更多的方法包括使用as,is等等)
-
使用局部变量(推荐)
double bar() { var i = this.i; // <-- Use of local variable. if (i != null) return i.toDouble(); return -1; } -
用 ?。和 ??
double bar() { return i?.toDouble() ?? -1; // Provide some default value. } -
使用 Bang 运算符 (!)
只有在 100% 确定变量 (
i) 永远不会是null.double bar() { return i!.toDouble(); // <-- Bang operator in play. }