迁移到 Dart 空安全后,“无法无条件调用运算符,因为接收器可以为空”错误

Sur*_*gch 18 dart type-promotion flutter dart-null-safety

我正在升级一个基于 Flutter 框架的个人包。我在 Flutter Text 小部件源代码中注意到这里有一个空检查:

if (textSpan != null) {
  properties.add(textSpan!.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition));
}
Run Code Online (Sandbox Code Playgroud)

但是,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.
Run Code Online (Sandbox Code Playgroud)

这是一个独立的示例:

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.
Run Code Online (Sandbox Code Playgroud)

我收到一个编译时错误:

错误:“字符串?”类型的值 无法从函数“myString”返回,因为它的返回类型为“String”。

或者,如果我尝试获取,则会_mySting.length出现以下错误:

不能无条件访问属性“length”,因为接收者可以为“null”。

我认为进行空检查会提升_myString为不可空类型。为什么不呢?

我的问题在 GitHub 上得到了解决,所以我在下面发布了一个答案。

Sur*_*gch 27

Dart 工程师 Erik Ernst在 GitHub 上说

类型提升只适用于局部变量。... 实例变量的提升是不合理的,因为它可能会被运行计算并在每次调用时返回不同对象的 getter 覆盖。参见 dart-lang/language#1188用于讨论类似于类型提升但基于动态检查的机制,以及一些相关讨论的链接。

所以本地类型推广有效:

  String myMethod(String? myString) {
    if (myString == null) {
      return '';
    }
    
    return myString;
  }
Run Code Online (Sandbox Code Playgroud)

但是实例变量不会提升。为此,您需要使用!运算符手动告诉 Dart 在这种情况下您确定实例变量不为空:

class MyClass {
  String? _myString;
  
  String myMethod() {
    if (_myString == null) {
      return '';
    }
    
    return _myString!;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @gegobyte,是的(如果添加分号)。 (2认同)

Cop*_*oad 12

错误:

假设这是您的代码,您正在对实例变量进行空检查,但仍然看到错误:

class Foo {
  int? i = 0;

  double bar() {
    if (i != null) return i.toDouble(); // <-- Error
    return -1;
  }
}
Run Code Online (Sandbox Code Playgroud)

方法 'toDouble' 不能无条件调用,因为接收者可以为 'null'。

您在这样的代码中看到的错误是因为Getter 没有被提升为它们的不可为空的对应物。让我们来谈谈原因。


错误原因:

比方说,有一个类Bar扩展Foo和覆盖i变量并且不为其分配任何值(保留它null):

class Bar extends Foo {
  @override
  int? i;
}
Run Code Online (Sandbox Code Playgroud)

所以,如果你能做到

print(Bar().d() * 2);
Run Code Online (Sandbox Code Playgroud)

您会遇到运行时 null 错误,这就是禁止 getter 类型提升的原因。


解决方案:

我们需要从int?. 通常有3种方法可以做到这一点(更多的方法包括使用asis等等)