如何在构造函数中初始化 final 类属性?

在 Java 中,你可以这样做:

class A {
private final int x;


public A() {
x = 5;
}
}

在达特,我试过:

class A {
final int x;


A() {
this.x = 5;
}
}

我得到两个编译错误:

最后一个变量‘ x’必须被初始化。

还有

‘ x’不能用作 setter,因为它的 final。

是否有方法在 Dart 中的构造函数中设置 final 属性?

76464 次浏览

您可以在构造函数体中实例化 final 字段。有一种特殊的语法:

class Point {
final num x;
final num y;
final num distanceFromOrigin;


// Old syntax
// Point(x, y) :
//   x = x,
//   y = y,
//   distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));


// New syntax
Point(this.x, this.y) :
distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
}

您可以使用构造函数中的 this.语法使它更短(在 https://www.dartlang.org/guides/language/language-tour#constructors中描述) :

class Point {
final num x;
final num y;
final num distanceFromOrigin;


Point(this.x, this.y)
: distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
}

如果你有一些更复杂的初始化,你应该使用工厂构造函数,代码变成:

class Point {
final num x;
final num y;
final num distanceFromOrigin;


Point._(this.x, this.y, this.distanceFromOrigin);


factory Point(num x, num y) {
num distance = distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
return new Point._(x, y, distance);
}
}

这里我遇到了一个难题,我希望初始化一个没有项目的 final List,并且在构造函数中定义一个 Stream(就像本例中的 distanceFromOrigin)。

对于下面的任何一个答案,我都无法做到这一点,但是我把它们混合在一起,结果奏效了。

例如:

class MyBloc {
final BehaviorSubject<List<String>> itemsStream;
final List<String> items = [];


MyBloc() : this.itemsStream = BehaviorSubject<List<String>>.seeded([]) {
items.addAll(List.generate(20, (index) => "Hola! I'm number $index"));
itemsStream.add(items);
}
}

我也遇到过类似的问题: 我试图从构造函数初始化一个 final字段,同时调用一个超级构造函数。您可以想到下面的例子

class Point2d {
final int x;
final int y;


Point2d.fromCoordinates(Coordinates coordinates)
: this.x = coordinates.x,
this.y = coordinates.y;
}


class Point3d extends Point2d {
final int z;


Point3d.fromCoordinates(Coordinates coordinates)
:this.z = coordinates.z,
super.fromCoordinates(coordinates);
}
/// Demo class, to simulate constructing an object
/// from another object.
class Coordinates {
final int x;
final int y;
final int z;
}

很明显这个管用。您可以使用上面的语法(检查 Point3d 的构造函数)初始化您的最终字段,它工作得很好!

运行一个像这样的小程序来检查:

void main() {
var coordinates = Coordinates(1, 2, 3);
var point3d = Point3d.fromCoordinates(coordinates);
print("x: ${point3d.x}, y: ${point3d.y}, z: ${point3d.z}");
}

应该能打印 x: 1, y: 2, z: 3

下面是初始化最终类变量的方法的简化摘要。

class MyClass {
final int x; //     <-- initialize this
}

初始值

class MyClass {
final int x = 'hello'.length;
}

如果初始化只能在运行时完成,那么只能使用 final。否则,使用 static const更好:

class MyClass {
static const int x = 0;
}

初始化程序正式

class MyClass {
MyClass(this.x);
final int x;
}

这是最常见的方法。

初始化列表

class MyClass {
MyClass(int x)
: _x = x;
final int _x;
}

当您希望保持字段私有时,这非常有用。

默认参数值

对于未命名的参数,可以用方括号([])围绕参数,对于命名的参数,可以用大括号({})围绕参数,然后给它一个默认值。

class MyClass {
MyClass({this.x = 0});
final int x;
}

如果您想让参数成为可选的,那么这是非常有用的。

您也可以使用初始化器列表来完成同样的事情:

class MyClass {
MyClass({int? x})
: _x = x ?? 0;
final int _x;
}

延迟初始化

class MyClass {
MyClass(String? a) {
x = a?.length ?? 0;
}
late final int x;
}

如果需要进行比初始值设定项清单中所允许的更复杂的初始化,这是有用的。例如,我在 Flutter 初始化一个手势识别器时就是这样做的。

惰性初始模式

使用 late的另一个优点是,在访问值之前它不会初始化值。

class MyClass {
late final int x = _doHeavyTask();
int _doHeavyTask() {
var sum = 0;
for (var i = 0; i < 100000000; i++) {
sum += 1;
}
return sum;
}
}

如果需要进行大量计算,而且只有在绝对需要的时候才需要调用它,那么这种方法非常有用。

这不会初始化 x:

final myClass = MyClass();

但这确实初始化了 x:

final myClass = MyClass();
final value = myClass.x;
class A{
final int x;
  

A(this.x){
}
  

}