首页 > 解决方案 > 为什么不能在 Dart 的构造函数体中初始化不可为空的字段?

问题描述

通常我有一个需要在构造函数中初始化的实例字段。例如,它可能需要根据其他实例字段进行计算,因此我不能内联(在声明的地方)或使用构造函数初始化器列表对其进行初始化。

但是,如果我需要在构造函数中对其进行初始化,它要么必须是可空的,要么必须是声明的。late关键字背后的基本原理late是程序员声明“我将在使用之前初始化它,相信我”,当编译器无法确定初始化将在首次使用之前发生时。但是:这个“程序员保证”在构造函数的情况下似乎 A) 糟糕和 B) 不必要,因为它可以由编译器确定该字段是否在构造函数中初始化(并且构造函数本身显然保证在任何其他实例方法之前执行) .

late在这种情况下使用字段的明显缺点是,没有什么会强制它们在编译时在构造期间(或任何地方,就此而言)实际初始化。另外,每次late读取该字段时,都会插入运行时检查以确保已为其分配了一个值 - 当我在构造函数中初始化时,我不需要它。

因此,从技术上讲,似乎应该有可能late在构造函数主体中初始化不可为空的非字段(如果不是,编译器可能会抛出错误)。

那么要求构造函数初始化的字段可以为空或声明为的基本原理是late什么?施加这种限制是否有技术原因,还是仅仅是 Dart 团队的设计疏忽?

标签: flutterdartlanguage-designdart-null-safety

解决方案


Dart从基类到派生类,由内而外地执行构造函数体。这允许在构造函数主体中发生虚拟调度。虚拟调度可以发生在构造函数体中的事实意味着编译器无法静态确定构造函数体将执行哪些代码,因此它无法推断构造函数体最终可能初始化什么。

构造函数主体可以执行可能初始化成员或可能依赖于已初始化成员的任意代码(包括回调),这使得它更加复杂。

此外,允许在构造函数主体运行时不初始化成员将容易出错并且会造成混淆。例如,使用:

class SomeClass {
  int member;

  SomeClass() {
    updateMember(0);
  }

  void updateMember(int value) {
    print(value); // Oops.
    member = value;
  }
}

使用 Dart 当前的设计,可以保证所有实例方法(及其覆盖)在调用方法时初始化成员。如果在执行构造函数主体时允许未初始化成员,那将不再是正确的,那么所有实例方法都需要考虑是否可以从构造函数(或从基类构造函数)调用它们,可能间接地从其他方法调用,以及访问的成员是否可能尚未初始化。

(我承认前一点并不是非常强大,因为目前仍然可能发生成员被初始化为构造函数主体必须改变的对象,但通常接收空的实例方法ListMap等等。问题比接收未初始化的成员。上述情况也可能发生在late成员身上,但这是选择使用的包袱late。)

Null-safety 不允许访问未初始化的非late变量,但您的建议将使这成为可能。

此外,由于通过初始化列表和通过构造函数体初始化成员之间存在区别,因此鼓励人们尽可能使用初始化列表。


推荐阅读