分配非空值文字时的空安全类型提升

Null safety type promotion when assigning non-null value literal

nullsafety.dartpad.dev如果我写下面的代码:

void main() {
  String? name = 'Bob';
  print(name.length);
}

我收到以下编译时错误:

An expression whose value can be 'null' must be null-checked before it can be dereferenced

以及以下运行时错误:

Property 'length' cannot be accessed on 'String?' because it is potentially null.

Type promotion on null checks 文档说:

The language is also smarter about what kinds of expressions cause promotion. An explicit == null or != null of course works. But explicit casts using as, or assignments, or the postfix ! operator we’ll get to soon also cause promotion. The general goal is that if the code is dynamically correct and it’s reasonable to figure that out statically, the analysis should be clever enough to do so.

问题

在上面的代码中 name 不可能为 null。该文档还说赋值应该导致类型提升。我是否误解了类型提升或者这是 DartPad 中的错误?

澄清

由于一些答案提供了针对错误消息的变通解决方案,我应该澄清一下,我并不是要解决上面的编码问题。相反,我是说我认为代码应该像它那样工作。但事实并非如此。为什么不呢?

我明白你在说什么。试试这个。

为了使类型提升起作用,您必须首先确认该值不为文档所述的空值。

正如您在图片中看到的那样,dart 能够进行类型提升或理解名称不会为空,因为它事先在 if 语句中进行了检查。

但是如果在 if 语句之外使用它而不事先检查它是否不为 null,dart 知道它可以随时再次赋值 null。这就是为什么它鼓励始终检查它是否为空。因为任何已实例化的变量(已赋值的变量)都可以在将来被赋值为null

此答案是对添加到原始问题的赏金的回应。赏金如下:

Please explain how String? is different from String and how type promotion works in Dart.

字符串?与字符串

类型 String? 可以包含字符串或 null。以下是一些示例:

String? string1 = 'Hello world';
String? string2 = 'I ❤️ Dart';
String? string3 = '';
String? string4 = null;

另一方面,String 类型只能包含字符串(一旦空安全成为 Dart 的一部分,就是这样)。它不能包含 null。以下是一些示例:

String string1 = 'Hello world';
String string2 = 'I ❤️ Dart';
String string3 = '';

如果您尝试执行以下操作:

String string4 = null;

您将收到 compile-time 错误:

A value of type 'Null' can't be assigned to a variable of type 'String'.

String 类型不能是 null,就像它不能是像 3 这样的 int 或像 [=27 这样的 bool =].这就是空安全的意义所在。如果你有一个类型为 String 的变量,你可以保证该变量永远不会是 null.

类型推广的工作原理

如果编译器可以从逻辑上确定可空类型(如 String?)永远不会是 null,那么它会将类型转换(或提升)为它的 non-nullable 对应类型(喜欢 String).

这是一个真实的例子:

void printNameLength(String? name) {
  if (name == null) {
    return;
  }
  print(name.length);
}

虽然参数name可以为空,但如果它实际上是null那么函数returns就早了。当你到达 name.length 时,编译器肯定知道 name 不能是 null。因此编译器将名称从 String? 提升为 String。表达式 name.length 永远不会导致崩溃。

这里有一个类似的例子:

String? name;
name = 'Bob';
print(name.length);

虽然 name 在这里也可以为空,但字符串文字 'Bob' 显然是 non-null。这也导致 name 被提升为 non-nullable String.

最初的问题是关于以下内容的:

String? name = 'Bob';
print(name.length);

似乎这也应该将 name 提升为 non-nullable String,但事实并非如此。正如@lrn(一名 Google 工程师)在 中指出的那样,这是一个错误,当空安全出现时,这也将像前面的示例一样工作。即name会升为non-nullableString.

进一步阅读