Dart 中成员变量的初始化方式有区别吗?
Is there a difference in how member variables are initialized in Dart?
在 Dart 中,立即赋值与在 Java 中的构造函数中赋值有区别吗?
class Example {
int x = 3;
}
对
class Example {
int x;
Example() {
x = 3;
}
}
我问是因为当我使用 Flutter 并试图将一个使用 setState 的函数分配给一个变量时,前一种方法不可能,但后者可以。
在你这种微不足道的情况下,这无关紧要。
一般来说,您可以通过以下几种方式初始化实例变量:
内联(字段初始值设定项)
class Example1 {
T x = value;
}
优点:
- 直接、简洁。
- 成员将在所有个构造函数中初始化。
- 可用于初始化
final
或non-nullable个成员。
- 成员在调用基 class 构造函数之前初始化,这在基 class 构造函数调用被派生的 class.
覆盖的成员函数时很重要
缺点:
- 不能依赖构造参数。
- 通常不能依赖于
this
,因为初始化发生在 this
生效之前(即,不能依赖于其他实例成员)。 (An exception is if the member is initialized lazily by declaring it late
。这需要启用 null-safety 功能。)
初始化器列表
class Example2 {
T x;
Example2() : x = value;
}
优点:
- 可用于初始化
final
或non-nullable个成员。
- 成员在调用基 class 构造函数之前初始化,这在基 class 构造函数调用被派生的 class.
覆盖的成员函数时很重要
- 可以利用构造参数。
- 初始化变量总是引用成员变量,从不引用构造函数参数。
缺点:
- 如果class有多个构造函数,初始化需要重复,或者构造函数应该重定向到一个公共构造函数。
- 不能依赖于
this
,因为初始化发生在 this
生效之前(即,不能依赖于其他实例成员)。
- 只能初始化封闭 class 的成员。因为初始化列表是在调用基 class 构造函数之前执行的,所以它们不能设置基 class 成员。
构造体
class Example3 {
T x;
Example3() {
x = value;
}
}
优点:
- 可以利用构造参数。
- 可用于执行更复杂的初始化,例如无法通过单个表达式初始化成员的情况。
- 可以使用
this
(即可以使用其他实例成员)。
- 可用于设置基础 class 成员。
缺点:
- 不能用于初始化非
late
final
或 non-nullable 成员。
- 如果class有多个构造函数,则需要重复初始化或重构初始化代码(例如但不限于重定向到通用构造函数)。
- 成员在 调用基础 class 构造函数后初始化。
- 如果构造函数有一个隐藏成员变量的参数,很容易不小心引用参数而不是成员。 (有关详细信息,请参阅 https://github.com/dart-lang/linter/issues/2552。)
我可能忘记了一些要点,但我认为应该涵盖了主要的要点。
直接,内联初始化首先发生,然后是初始化列表,然后是构造函数体。另见 ,它解释了为什么 this
仅对对象初始化的后期有效。
作为重要的成员初始化位置的示例:
class Base {
Base() {
doSomething();
}
void doSomething() {}
}
class DerivedEarly extends Base {
int? x;
DerivedEarly() : x = 42;
@override
void doSomething() => print(x);
}
class DerivedLate extends Base {
int? x;
DerivedLate() {
x = 42;
}
@override
void doSomething() => print(x);
}
void main() {
DerivedEarly(); // Prints: 42
DerivedLate(); // Prints: null
}
尽可能使用初始化形式使您的代码更简洁。
class Point {
double x, y;
Point(this.x, this.y);
}
这个this
。构造函数参数之前的语法称为“初始化形式”。你不能总是利用它。有时您希望有一个命名参数,其名称与您正在初始化的字段的名称不匹配。但是当你可以使用初始化形式时,你应该。
class Point {
final num x;
final num y;
final num distanceFromOrigin;
// Special syntax
Point(this.x, this.y) :
distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
}
在 Dart 中,立即赋值与在 Java 中的构造函数中赋值有区别吗?
class Example {
int x = 3;
}
对
class Example {
int x;
Example() {
x = 3;
}
}
我问是因为当我使用 Flutter 并试图将一个使用 setState 的函数分配给一个变量时,前一种方法不可能,但后者可以。
在你这种微不足道的情况下,这无关紧要。
一般来说,您可以通过以下几种方式初始化实例变量:
内联(字段初始值设定项)
class Example1 {
T x = value;
}
优点:
- 直接、简洁。
- 成员将在所有个构造函数中初始化。
- 可用于初始化
final
或non-nullable个成员。 - 成员在调用基 class 构造函数之前初始化,这在基 class 构造函数调用被派生的 class. 覆盖的成员函数时很重要
缺点:
- 不能依赖构造参数。
- 通常不能依赖于
this
,因为初始化发生在this
生效之前(即,不能依赖于其他实例成员)。 (An exception is if the member is initialized lazily by declaring itlate
。这需要启用 null-safety 功能。)
初始化器列表
class Example2 {
T x;
Example2() : x = value;
}
优点:
- 可用于初始化
final
或non-nullable个成员。 - 成员在调用基 class 构造函数之前初始化,这在基 class 构造函数调用被派生的 class. 覆盖的成员函数时很重要
- 可以利用构造参数。
- 初始化变量总是引用成员变量,从不引用构造函数参数。
缺点:
- 如果class有多个构造函数,初始化需要重复,或者构造函数应该重定向到一个公共构造函数。
- 不能依赖于
this
,因为初始化发生在this
生效之前(即,不能依赖于其他实例成员)。 - 只能初始化封闭 class 的成员。因为初始化列表是在调用基 class 构造函数之前执行的,所以它们不能设置基 class 成员。
构造体
class Example3 {
T x;
Example3() {
x = value;
}
}
优点:
- 可以利用构造参数。
- 可用于执行更复杂的初始化,例如无法通过单个表达式初始化成员的情况。
- 可以使用
this
(即可以使用其他实例成员)。 - 可用于设置基础 class 成员。
缺点:
- 不能用于初始化非
late
final
或 non-nullable 成员。 - 如果class有多个构造函数,则需要重复初始化或重构初始化代码(例如但不限于重定向到通用构造函数)。
- 成员在 调用基础 class 构造函数后初始化。
- 如果构造函数有一个隐藏成员变量的参数,很容易不小心引用参数而不是成员。 (有关详细信息,请参阅 https://github.com/dart-lang/linter/issues/2552。)
我可能忘记了一些要点,但我认为应该涵盖了主要的要点。
直接,内联初始化首先发生,然后是初始化列表,然后是构造函数体。另见 this
仅对对象初始化的后期有效。
作为重要的成员初始化位置的示例:
class Base {
Base() {
doSomething();
}
void doSomething() {}
}
class DerivedEarly extends Base {
int? x;
DerivedEarly() : x = 42;
@override
void doSomething() => print(x);
}
class DerivedLate extends Base {
int? x;
DerivedLate() {
x = 42;
}
@override
void doSomething() => print(x);
}
void main() {
DerivedEarly(); // Prints: 42
DerivedLate(); // Prints: null
}
尽可能使用初始化形式使您的代码更简洁。
class Point {
double x, y;
Point(this.x, this.y);
}
这个this
。构造函数参数之前的语法称为“初始化形式”。你不能总是利用它。有时您希望有一个命名参数,其名称与您正在初始化的字段的名称不匹配。但是当你可以使用初始化形式时,你应该。
class Point {
final num x;
final num y;
final num distanceFromOrigin;
// Special syntax
Point(this.x, this.y) :
distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
}