Primary constructors(主构造函数)
主构造函数(primary constructor)把类的主要构造参数直接写在类头部,并可通过参数同时声明字段,从而减少重复代码。它只缩短声明方式,不改变对象的运行时模型。
版本说明:主构造函数在 Dart 3.12 中仍是实验性预览,需要显式启用
primary-constructors实验。实验语法和限制可能在稳定前调整,不宜直接用于要求稳定兼容的生产代码。
从传统写法到主构造函数
传统类需要分别声明字段和构造函数:
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
启用实验后,可以把它写成:
class Point(final int x, final int y);
类名后的参数列表就是主构造函数。空类体可以用分号代替 {},因此简单数据类能够保持在一行内。
声明参数与普通参数
主构造函数参数带有 var 或 final 时称为声明参数(declaring parameter),它会隐式创建同名实例字段:
class MutablePoint(var int x, var int y);
class ImmutablePoint(final int x, final int y);
省略 var 或 final 的参数只是构造参数,不会创建字段:
class User(String fullName) {
final String displayName = fullName.trim();
}
这里的 fullName 可以参与非 late 字段初始化,但实例创建后不存在 user.fullName 成员。
参数作用域
主构造函数参数在两个位置具有不同的解析规则:
- 在非
late字段初始化器和this :初始化列表中,名称指向原始构造参数。 - 在主构造函数体中,声明参数的名称指向它创建的字段;普通参数仍指向参数本身。
class Label(var String text, String suffix) {
final String original = text;
this {
text = text.toUpperCase();
print('$text$suffix');
}
}
void main() {
final label = Label('dart', '!'); // DART!
print(label.original); // dart
print(label.text); // DART
}
字段初始化器看到的是传入的 'dart';进入 this 构造函数体后,text 指向可变实例字段,因此赋值会改变对象状态。
添加初始化列表和函数体
需要断言、额外初始化或执行语句时,在类体中使用 this 声明主构造函数的初始化部分和函数体:
class Percentage(final int value) {
this : assert(value >= 0 && value <= 100) {
print('Created: $value%');
}
}
只有初始化列表而没有函数体时,可以用分号结束:
class PositiveInt(final int value) {
this : assert(value > 0);
}
常量与命名主构造函数
字段满足常量要求时,可以在类名之前加入 const:
class const Point(final int x, final int y);
void main() {
const point = Point(1, 2);
print(point.x); // 1
}
也可以在类名后添加名称,声明命名主构造函数:
class Point.custom(var int x, var int y);
void main() {
final point = Point.custom(1, 2);
print(point.y); // 2
}
名称可以是私有名称,例如 class Point._(...),用于限制库外代码直接调用。
转发超类参数
与传统构造函数相同,主构造函数可以使用 super 参数把值转发给超类:
class Person(final String name);
class Employee(final int id, super.name) extends Person;
super.name 不会在 Employee 中创建第二个 name 字段,它负责向 Person 的主构造函数传参。
启用实验
运行单个 Dart 程序时,可以使用实验参数:
dart run --enable-experiment=primary-constructors bin/main.dart
编辑器、分析器、测试和构建命令也必须使用支持该实验的 Dart 3.12 SDK,并以相应方式启用实验。仅修改一条运行命令,不能保证整个工具链都能解析该语法。
TypeScript 对比:TypeScript 的参数属性可以在
constructor(public x: number)中同时声明并初始化字段。Dart 主构造函数也减少了字段与参数的重复,但参数位于类头部,并且截至 Dart 3.12 仍属实验性功能,两者并不完全等价。
使用限制
- 声明参数专用的
var、final不能随意用于普通函数参数。 - 拥有主构造函数的类不能再声明其他非重定向的生成式构造函数,以确保创建实例时必定经过主构造函数。
mixin class的主构造函数受到更严格限制,只能使用无参数、无函数体和无初始化列表的形式。- 扩展类型的主构造函数仍必须只有一个表示底层值的参数,不能用
var声明可变表示字段。 - 实验功能可能出现破坏性调整,升级 SDK 前应重新核对发布说明。
小结
- 主构造函数把主要参数写在类头部,
var或final参数会同时声明字段。 - 普通参数不会生成字段,但可用于字段初始化和主构造函数体。
this块用于补充初始化列表、断言和构造函数体。- 主构造函数支持常量、命名形式和
super参数,但与其他生成式构造函数组合时有限制。 - Dart 3.12 中该功能仍需实验标志,不应当作稳定语法使用。