文章

Dart变量

Dart中的变量声明 变量默认值 Final和Const

Dart语言详解

初始化

创建一个变量并进行初始化:

1
var name = 'Bob';

变量会保存引用。name 变量包含一个值为 “Bob” 的 String 对象的引用。

变量 name 的类型被推断为 String,可以通过指定类型来更改它。如果一个对象不受限于单一类型,可以指定为 Object 类型(或在必要时使用 dynamic)。

1
Object name = 'Bob';

另一种选择是显式声明将要被推断的类型:

1
String name = 'Bob';

空安全

Dart 语言要求以健全的空安全方式编写代码。

空安全能够防止意外访问 null 的变量而导致的错误。这样的错误也被称为空解引用错误。访问一个求值为 null 的表达式的属性或调用方法时,会发生空解引用错误。但是对于 toString() 方法和 hashCode 属性,空安全会体现出例外情况。 Dart 编译器可以在空安全的基础上在编译期检测到这些潜在的错误。

控制变量是否允许 null ,可以在类型声明的末尾添加 ? 。

1
2
3
String? name  // Nullable type. Can be `null` or string.

String name   // Non-nullable type. Cannot be `null` but can be string.

空安全将潜在的 运行时错误 转变为 编辑时 分析错误。当非空变量处于以下任一状态时,空安全会识别该变量:

  • 未使用非空值进行初始化。
  • 赋值为 null 。

默认值

未初始化的变量默认值是 null。即使变量是数字 类型默认值也是 null,因为在 Dart 中一切都是对象,数字类型也不例外。

1
2
int? lineCount;
assert(lineCount == null);

提示: 在生产环境代码中 assert() 函数会被忽略,不会被调用。 在开发过程中, assert(condition) 会在非 true 的条件下抛出异常.

对于空安全,你必须在使用非空变量之前初始化它们的值:

1
int lineCount = 0;

不必在声明变量时初始化变量,但在使用之前需要为其赋值。例如以下代码是合法的,因为 Dart 可以检测到 lineCount 在传递给 print() 时是非空的:

1
2
3
4
5
6
7
8
9
int lineCount;

if (weLikeToCount) {
  lineCount = countLines();
} else {
  lineCount = 0;
}

print(lineCount);

顶级变量和类变量是延迟初始化的,它们会在第一次被使用时再初始化。

延迟初始化

late 修饰符有两种用法: - 声明一个非空变量,但不在声明时初始化。 - 延迟初始化一个变量。

通常 Dart 的语义分析可以检测非空变量在使用之前是否被赋值,但有时会分析失败。常见的两种情况是在分析顶级变量和实例变量时,Dart 通常无法确定它们是否已设值,因此不会尝试分析。

如果你确定变量在使用之前已设置,但 Dart 推断错误的话,可以将变量标记为 late 来解决这个问题。

1
2
3
4
5
6
late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

如果没有初始化一个 late 变量,那么当变量被使用时会发生运行时错误。

当一个 late 修饰的变量在声明时就指定了初始化方法,那么内容会在第一次使用变量时运行初始化。这种延迟初始化在以下情况很方便: - (Dart 推断)可能不需要该变量,并且初始化它的开销很高。 - 你正在初始化一个实例变量,它的初始化方法需要调用 this

1
2
// 如果 `temperature` 变量从未被使用,则 `readThermometer()` 这个开销较大的函数也永远不会被调用:
late String temperature = readThermometer(); // Lazily initialized.

Final 和 Const

如果不打算更改一个变量,可以使用 final 或 const 修饰它,而不是使用 var 或作为类型附加。

  • 一个 final 变量只能设置一次。
  • const 变量是编译时常量。(const 常量隐式包含了 final。)

提示: 实例变量可以是 final 但不能是 const 。 必须在构造函数体执行之前初始化 final 实例变量 —— 在变量声明中,参数构造函数中或构造函数的初始化列表中进行初始化。

创建和设置一个 Final 变量:

1
2
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

final 不能被修改:

1
2
 static analysis: failuredart
name = 'Alice'; // Error: a final variable can only be set once.

如果需要在编译时就固定变量的值,可以使用 const 类型变量。 如果

使用 const 修饰 编译时常量 的变量。如果 const 变量位于类级别,请将其标记为 static const(静态常量)。在声明变量的位置,将其值设置为编译时常量,比如数字、字符串、const 常量或在常量数字上进行的算术运算的结果:

1
2
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere

const 关键字不仅仅可用于声明常量,你还可以使用它来创建常量 值(values),以及声明 创建(create) 常量值的构造函数。任何变量都可以拥有常量值。

1
2
3
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

可以省略以 const 声明中的值的 const 修饰

1
const baz = []; // Equivalent to `const []`

变量没有被 final 或者 const 修饰,即使它以前被 const 修饰,你也可以修改这个变量

1
2
var foo = const [];
foo = [1, 2, 3]; // Was const []

不能修改 const 变量的值:

1
2
3
const baz = [];
 static analysis: failuredart
baz = [42]; // Error: Constant variables can't be assigned a value.

可以在定义常量时使用 类型检查和转换is 和 as)、 集合中的 if 和 展开操作符... 和 ...?):

1
2
3
4
const Object i = 3; // Where i is a const Object with an int value...
const list = [i as int]; // Use a typecast.
const map = {if (i is int) i: 'int'}; // Use is and collection if.
const set = {if (list is List<int>) ...list}; // ...and a spread.

虽然 final 对象不能被修改,但它的字段可能可以被更改。相比之下,const 对象及其字段不能被更改:它们是 不可变的

  • final 或者const 变量的值不可以修改:
  • Dart中的字段实际上是一个匿名存储位置,结合了自动创建的getter和setter来读取和更新存储,还可以在构造函数的初始化列表中对其进行初始化。
  • final字段是相同的,只是没有设置器,因此设置其值的唯一方法是在构造函数初始化器列表中,并且此后无法更改值-因此是“ final”。
  • final和const不同在于,const变量关键是创建编译时常量值:在编译时已经知道所有字段值的对象,而不执行任何语句。const声明必须用const类型的值初始化。
  • const 变量同时也是final变量。如果 const 变量在类中,必须定义为 static const。static 表示成员在类本身上可用,而不是在类的实例上
  • const 关键字不仅仅只用来定义常量,还可以用来创建不变的值,还能定义const类型的构造函数,这种类型的构造函数创建的对象是不可改变的。任何变量都可以有一个不变的值
本文由作者按照 CC BY 4.0 进行授权