跳到主要内容

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

类名后的参数列表就是主构造函数。空类体可以用分号代替 {},因此简单数据类能够保持在一行内。

声明参数与普通参数

主构造函数参数带有 varfinal 时称为声明参数(declaring parameter),它会隐式创建同名实例字段:

class MutablePoint(var int x, var int y);

class ImmutablePoint(final int x, final int y);

省略 varfinal 的参数只是构造参数,不会创建字段:

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 仍属实验性功能,两者并不完全等价。

使用限制

  • 声明参数专用的 varfinal 不能随意用于普通函数参数。
  • 拥有主构造函数的类不能再声明其他非重定向的生成式构造函数,以确保创建实例时必定经过主构造函数。
  • mixin class 的主构造函数受到更严格限制,只能使用无参数、无函数体和无初始化列表的形式。
  • 扩展类型的主构造函数仍必须只有一个表示底层值的参数,不能用 var 声明可变表示字段。
  • 实验功能可能出现破坏性调整,升级 SDK 前应重新核对发布说明。

小结

  • 主构造函数把主要参数写在类头部,varfinal 参数会同时声明字段。
  • 普通参数不会生成字段,但可用于字段初始化和主构造函数体。
  • this 块用于补充初始化列表、断言和构造函数体。
  • 主构造函数支持常量、命名形式和 super 参数,但与其他生成式构造函数组合时有限制。
  • Dart 3.12 中该功能仍需实验标志,不应当作稳定语法使用。