flutter - 为什么不能在 Dart 的构造函数体中初始化不可为空的字段?
问题描述
通常我有一个需要在构造函数中初始化的实例字段。例如,它可能需要根据其他实例字段进行计算,因此我不能内联(在声明的地方)或使用构造函数初始化器列表对其进行初始化。
但是,如果我需要在构造函数中对其进行初始化,它要么必须是可空的,要么必须是声明的。late
关键字背后的基本原理late
是程序员声明“我将在使用之前初始化它,相信我”,当编译器无法确定初始化将在首次使用之前发生时。但是:这个“程序员保证”在构造函数的情况下似乎 A) 糟糕和 B) 不必要,因为它可以由编译器确定该字段是否在构造函数中初始化(并且构造函数本身显然保证在任何其他实例方法之前执行) .
late
在这种情况下使用字段的明显缺点是,没有什么会强制它们在编译时在构造期间(或任何地方,就此而言)实际初始化。另外,每次late
读取该字段时,都会插入运行时检查以确保已为其分配了一个值 - 当我在构造函数中初始化时,我不需要它。
因此,从技术上讲,似乎应该有可能late
在构造函数主体中初始化不可为空的非字段(如果不是,编译器可能会抛出错误)。
那么要求构造函数初始化的字段可以为空或声明为的基本原理是late
什么?施加这种限制是否有技术原因,还是仅仅是 Dart 团队的设计疏忽?
解决方案
Dart从基类到派生类,由内而外地执行构造函数体。这允许在构造函数主体中发生虚拟调度。虚拟调度可以发生在构造函数体中的事实意味着编译器无法静态确定构造函数体将执行哪些代码,因此它无法推断构造函数体最终可能初始化什么。
构造函数主体可以执行可能初始化成员或可能依赖于已初始化成员的任意代码(包括回调),这使得它更加复杂。
此外,允许在构造函数主体运行时不初始化成员将容易出错并且会造成混淆。例如,使用:
class SomeClass {
int member;
SomeClass() {
updateMember(0);
}
void updateMember(int value) {
print(value); // Oops.
member = value;
}
}
使用 Dart 当前的设计,可以保证所有实例方法(及其覆盖)在调用方法时初始化成员。如果在执行构造函数主体时允许未初始化成员,那将不再是正确的,那么所有实例方法都需要考虑是否可以从构造函数(或从基类构造函数)调用它们,可能间接地从其他方法调用,以及访问的成员是否可能尚未初始化。
(我承认前一点并不是非常强大,因为目前仍然可能发生成员被初始化为构造函数主体必须改变的对象,但通常接收空的实例方法List
,Map
等等。问题比接收未初始化的成员。上述情况也可能发生在late
成员身上,但这是选择使用的包袱late
。)
Null-safety 不允许访问未初始化的非late
变量,但您的建议将使这成为可能。
此外,由于通过初始化列表和通过构造函数体初始化成员之间存在区别,因此鼓励人们尽可能使用初始化列表。
推荐阅读
- php - 如何加入单数组和多维数组php
- odata - 在 JS 中将 OData V2 Edm.Decimal 值转换为依赖于语言环境的十进制数
- python - train_data (1) 的大小不能小于 batch_size (32)
- python - 如何让蛇自己移动?Python
- c - 如何为指针打印空终止字符\ 0
- javascript - 一键式Java脚本图像转换器
- python - 当我在 python 中使用 while 循环运行命令时,我遇到了数量问题
- python - 我的程序可以在我的在线课程的 Python 自动评分器中运行,但不能在 Linux 上运行?
- python - socket连接后可以对受害者的机器进行哪些攻击?
- json - 从 API 获取对象列表反应原生