Dart 中的空安全是啥?

Posted

技术标签:

【中文标题】Dart 中的空安全是啥?【英文标题】:What is Null Safety in Dart?Dart 中的空安全是什么? 【发布时间】:2020-05-20 22:23:46 【问题描述】:

我听说了新的 Dart 空安全语言功能 (NNBD),目前是“'non-nullable' Experiment”。它应该默认引入 non-nullable

功能规范可以是found here,语言可以是GitHub issue here。

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

【问题讨论】:

恕我直言,Dart 团队已经结束了,就像 Java 强迫我们捕获所有异常一样。代码变得非常嘈杂。 第一个链接断开 (404)。 【参考方案1】:

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

目前可以在nullsafety.dartpad.dev 找到 null 安全/不可为空(默认)、简称 NNBD 的功能。

请记住,您可以阅读full spec here 和full roadmap here。现在,健全的 null 安全性也已成为 officially announced for Dart。


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

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

  word = 'Hello, ';
  print(word); // legal

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

void main() 
  String word;
  
  word = null; // forbidden
  world = 'World!'; // allowed

2.1.1。这对我有什么帮助?

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

int number = 4;

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

  int sum = number + 2; // allowed because number is also non-nullable

2.1.2。记住

如果类中的实例字段不能为空,则必须初始化

class Foo 
  String word; // forbidden

  String sentence = 'Hello, World!'; // allowed

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

2.2。可空类型 (?)

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

class Foo 
  String word; // forbidden

  String? sentence; // allowed

可为空的变量在使用前不需要初始化。默认初始化为null

void main() 
  String? word;
  
  print(word); // prints null

2.2.2。 !

如果e 为空,则将! 附加到任何变量e 将引发运行时错误,否则将其转换为不可为空的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

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';
  

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

2.3.1。 late final

最终变量现在也可以标记为延迟:

late final int x = heavyComputation();

这里的heavyComputation 只会在x 被访问时被调用。此外,您还可以声明一个不带初始化器的late final,这与只有一个late 变量相同,但只能分配一次。

late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden

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

2.4。 required

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

void allowed(required String word) => null;

这也意味着如果一个参数应该是不可为空的,它需要被标记为required或者有一个默认值:

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

void forbidden(int x) // compile-time error because x can be null (unassigned)
    =>
    null;

任何其他命名参数都必须是可为空的

void baz(int? x) => null;

2.5。 ?[]

为索引运算符[] 添加了可识别空值的?[] 运算符:

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

  int? x = list?[0]; // 1

另见this article about the syntax decision。

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);


2.6。 Never

下面的解释很烂。阅读"Top and bottom" from "Understanding null safety" 以获得好消息。

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

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

本质上,Never 意味着不允许任何类型,Never 本身不能被实例化。List&lt;Never&gt; 中只有 Never 满足列表的泛型类型约束,这意味着它必须为。但List&lt;Null&gt; 可以包含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)
];

示例:编译器将List&lt;Never&gt; 推断为 empty const List&lt;T&gt;Never 不应该被程序员使用就我而言。 (I was wrong)。

3。了解更多

您可以阅读official article on sound null safety。 此外,如开头所述,您可以play with it on DartPad。

【讨论】:

你能给出一些可以使用Never的场景吗? 我们实际上已经决定使用“?[]”作为空感知索引运算符,而不是“?.[]”。后者在语法上稍微复杂一些,但这是用户想要的。 @RamsesAldama 我添加了一些东西。我链接到的规范提到了更多。 应该指出,成员或实例变量上的late final 仅在运行时检查。由于停机问题,无法在开发时或编译时进行检查。所以你不会得到 IDE 的帮助。 现在有没有办法在默认情况下使用非空值编译代码,或者 Dartpad 是唯一可以尝试的地方?【参考方案2】:

如果您希望此字段为必填项,请使用 required 关键字。否则,您只需要添加“?”。像这样:

const phonefield(
    Key? key, required this.onchanged,
) : super(key: key);

final  ValueChanged<String>onchanged;

【讨论】:

以上是关于Dart 中的空安全是啥?的主要内容,如果未能解决你的问题,请参考以下文章

▩Dart-空安全(Null Safety)

是否可以为依赖的 Dart 包禁用空安全性?

▩Dart-深入理解空安全

Python中的空返回是啥意思? [复制]

Dart 中的快照概念是啥?

检查输入字段中的空值的正确方法是啥[重复]