Dart 中的空安全性是什么?

cre*_*not 89 dart flutter

我听说过新的 Dart 空安全语言功能 (NNBD),目前是“ '不可空'实验”。它应该默认引入不可为空

可以在此处找到功能规范,在此处找到语言GitHub 问题

它是如何工作的,我可以在哪里试用?

cre*_*not 223

1.空安全/不可空(默认)

空安全/不可空(默认情况下),简称 NNBD,功能目前可以在nullsafety.dartpad.dev找到。

请记住,您可以在这里阅读完整的规范完整的路线图。现在,Dart正式宣布了声音零安全性。


2.1. 默认情况下不可为空是什么意思?

void main() {
  String word;
  print(word); // illegal

  word = 'Hello, ';
  print(word); // legal
}
Run Code Online (Sandbox Code Playgroud)

正如您在上面看到的,默认情况下不可为空变量意味着通常声明的每个变量都不能null. 因此,在变量被赋值之前访问变量的任何操作都是非法的。
此外,null也不允许分配给不可为空的变量:

void main() {
  String word;
  
  word = null; // forbidden
  world = 'World!'; // allowed
}
Run Code Online (Sandbox Code Playgroud)

2.1.1. 这对我有什么帮助?

如果一个变量是不可为空的,你可以确定它是 never null。因此,您永远不需要事先检查它。

int number = 4;

void main() {
  if (number == null) return; // redundant

  int sum = number + 2; // allowed because number is also non-nullable
}
Run Code Online (Sandbox Code Playgroud)

2.1.2. 记住

如果类中的实例字段不可为空,则必须对其进行初始化

class Foo {
  String word; // forbidden

  String sentence = 'Hello, World!'; // allowed
}
Run Code Online (Sandbox Code Playgroud)

请参阅late下文以修改此行为。

2.2. 可空类型 ( ?)

您可以通过将问号附加到变量类型来使用可为空类型?

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}
Run Code Online (Sandbox Code Playgroud)

一个可空变量不需要初始化,然后才能使用它。它null默认初始化为:

void main() {
  String? word;
  
  print(word); // prints null
}
Run Code Online (Sandbox Code Playgroud)

2.2.2. !

附加!到任何变量e将引发运行时错误,如果e为 null,否则将其转换为不可为 null 的v

void main() {
  int? e = 5;
  int v = e!; // v is non-nullable; would throw an error if e were null

  String? word;
  print(word!); // throws runtime error if word is null

  print(null!); // throws runtime error
}
Run Code Online (Sandbox Code Playgroud)

2.3. late

该关键字late可用于标记稍后将被初始化的变量,即不是在声明时而是在访问时进行初始化。这也意味着我们可以拥有稍后初始化的不可为空的实例字段

class ExampleState extends State {
  late final String word; // non-nullable

  @override
  void initState() {
    super.initState();

    // print(word) here would throw a runtime error
    word = 'Hello';
  }
}
Run Code Online (Sandbox Code Playgroud)

word在初始化之前访问将引发运行时错误。

2.3.1. late final

最终变量现在也可以标记为后期:

late final int x = heavyComputation();
Run Code Online (Sandbox Code Playgroud)

这里heavyComputation只会被调用一次x被访问。另外,你也可以声明一个late final没有初始化器,这和只有一个late变量是一样的,但它只能被赋值一次。

late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden
Run Code Online (Sandbox Code Playgroud)

请注意,现在将评估所有具有初始值设定项的顶级静态变量late,无论它们是否为final

2.4. required

以前是注释( @required),现在作为修饰符内置。它允许将任何命名参数(对于函数或类)标记为required,这使它们不可为空:

void allowed({required String word}) => null;
Run Code Online (Sandbox Code Playgroud)

这也意味着如果一个参数应该是non-nullable,它需要被标记为required或有一个默认值:

void allowed({String word = 'World'}) => null;

void forbidden({int x}) // compile-time error because x can be null (unassigned)
    =>
    null;
Run Code Online (Sandbox Code Playgroud)

任何其他命名参数都必须可以为

void baz({int? x}) => null;
Run Code Online (Sandbox Code Playgroud)

2.5. ?[]

?[]为索引运算符添加了空感知运算符[]

void main() {
  List<int>? list = [1, 2, 3];

  int? x = list?[0]; // 1
}
Run Code Online (Sandbox Code Playgroud)

另请参阅有关语法决策的这篇文章

2.5.1. ?..

级联运算符现在还有一个新的空感知运算符:?..

它会导致以下级联操作仅在接收者不为 null 时执行。因此,?..必须是级联序列中的第一个级联运算符:

void main() {
  Path? path;

  // Will not do anything if path is null.
  path
    ?..moveTo(3, 4)
    ..lineTo(4, 3);

  // This is a noop.
  (null as List)
    ?..add(4)
    ..add(2)
    ..add(0);
}
Run Code Online (Sandbox Code Playgroud)

2.6. Never

下面的解释很烂。阅读“了解零安全性”中的“顶部和底部”以获得很好的效果。

为避免混淆:这不是开发人员必须担心的事情。为了完整起见,我想提及它。

Never将是一种类似于之前存在的Null不是null)中定义的类型dart:core。这两个类都不能扩展、实现或混合,因此不打算使用它们。

本质上,Never意味着不允许任何类型,并且Never它本身不能被实例化。
无外乎Neverin a List<Never>满足列表的泛型类型约束,这意味着它必须是emptyList<Null>,但是,可以包含null

// Only valid state: []
final neverList = <Never>[
  // Any value but Never here will be an error.
  5, // error
  null, // error

  Never, // not a value (compile-time error)
];

// Can contain null: [null]
final nullList = <Null>[
  // Any value but Null will be an error.
  5, // error
  null, // allowed

  Never, // not a value (compile-time error)
  Null, // not a value (compile-time error)
];
Run Code Online (Sandbox Code Playgroud)

示例:编译器会推断 List<Never>空的 const List<T>.
Never就我而言,程序员不应该使用它。(我错了)。

3. 了解更多

您可以阅读有关 sound null safety官方文章
此外,如开头所述,您可以在 DartPad 上使用它

  • 你能给出一些可以使用“Never”的场景吗? (12认同)
  • 我们实际上决定使用“?[]”作为空感知索引运算符,而不是“?.[]”。后者在语法上稍微复杂一些,但这是用户想要的。 (2认同)