变量

Last updated: ... / Reads: 44 Edit

这是创建变量并初始化它的示例:

var name = 'Bob';

变量存储引用。名为 name 的变量包含对值为“Bob”的 String 对象的引用。

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

Object name = 'Bob';

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

String name = 'Bob';

注意:此页面遵循样式指南建议,对局部变量使用 var 而不是类型注释。

空安全

空安全可防止因无意访问设置为 null 的变量而导致的错误。该错误称为空取消引用错误。当您访问属性或对计算结果为 null 的表达式调用方法时,会发生空取消引用错误。此规则的一个例外是 null 支持属性或方法,例如 toString() 或 hashCode 。通过 null 安全性,Dart 编译器可以在编译时检测到这些潜在错误。

例如,假设您想要查找 int 变量 i 的绝对值。如果 i 为 null ,则调用 i.abs() 会导致空取消引用错误。在其他语言中,尝试这样做可能会导致运行时错误,但 Dart 的编译器禁止这些操作。因此,Dart 应用程序不会导致运行时错误。

空安全引入了三个关键变化:

  1. 当您为变量、参数或其他相关组件指定类型时,您可以控制该类型是否允许 null 。要启用可为空性,请将 ? 添加到类型声明的末尾。
String? name  // 可为 null 的类型。可以是“null”或字符串。

String name   // 不可为 null 的类型。不能为“null”,但可以是字符串。

  1. 使用变量之前必须对其进行初始化。可空变量默认为 null ,因此默认情况下会初始化它们。 Dart 不会将初始值设置为不可空类型。它强制您设置一个初始值。 Dart 不允许您观察未初始化的变量。这可以防止您访问接收者类型可以为 null 但 null 不支持所使用的方法或属性的属性或调用方法。

  2. 您无法访问具有可为 null 类型的表达式的属性或调用方法。同样的例外也适用于 null 支持的属性或方法,例如 hashCode 或 toString() 。

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

  • 未使用非空值进行初始化。
  • 分配了一个 null 值。

通过此检查,您可以在部署应用程序之前修复这些错误。

默认值

具有可为 null 类型的未初始化变量的初始值为 null 。即使是数字类型的变量最初也是 null,因为数字(与 Dart 中的其他所有内容一样)都是对象。

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

注意:生产代码会忽略 assert() 调用。另一方面,在开发过程中,如果条件为 false, assert(condition) 将引发异常。有关详细信息,请查看断言。

对于 null 安全性,您必须在使用不可为 null 的变量之前初始化它们的值:

int lineCount;

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

print(lineCount);

顶级变量和类变量是延迟初始化的;初始化代码在第一次使用变量时运行。

后期变量

late 修饰符有两个用例:

  • 声明一个不可为 null 的变量,该变量在声明后进行初始化。
  • 延迟初始化变量。 通常,Dart 的控制流分析可以检测不可空变量在使用前何时设置为非空值,但有时分析会失败。两种常见情况是顶级变量和实例变量:Dart 通常无法确定它们是否已设置,因此不会尝试。

如果您确定变量在使用之前已设置,但 Dart 不同意,您可以通过将变量标记为 late 来修复错误:

late String description;

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

如果未能初始化 late 变量,则在使用该变量时会发生运行时错误。

当您将变量标记为 late 但在声明时对其进行初始化时,初始化程序将在第一次使用该变量时运行。这种惰性初始化在以下几种情况下很方便:

  • 该变量可能不需要,并且初始化它的成本很高
  • 您正在初始化一个实例变量,其初始化程序需要访问 this 。

在以下示例中,如果从未使用 temperature 变量,则永远不会调用昂贵的 readThermometer() 函数:

// 这是程序对 readThermometer() 的唯一调用。
late String temperature = readThermometer(); // 延迟初始化。

Final和常量 如果您从不打算更改变量,请使用 final 或 const 来代替 var 或作为类型的补充。最终变量只能设置一次; const 变量是编译时常量。 (常量变量是隐式最终变量。)

实例变量可以是 final 但不能是 const 。

以下是创建和设置 final 变量的示例:

final name = 'Bob'; // 没有类型注释
final String nickname = 'Bobby';

您无法更改 final 变量的值:

name = 'Alice'; //错误:最终变量只能设置一次。

对于要成为编译时常量的变量,请使用 const 。如果 const 变量位于类级别,则将其标记为 static const 。在声明变量的地方,将值设置为编译时常量,例如数字或字符串文字、const 变量或常量算术运算的结果:

const bar = 1000000; // 压力单位 (dynes/cm2)
const double atm = 1.01325 * bar; // 标准大气

const 关键字不仅仅用于声明常量变量。您还可以使用它来创建常量值,以及声明创建常量值的构造函数。任何变量都可以有一个常量值。

var foo = const [];
final bar = const [];
const baz = []; // 相当于 `const []`

您可以从 const 声明的初始化表达式中省略 const ,就像上面的 baz 一样。有关详细信息,请参阅不要重复使用 const。

您可以更改非最终、非常量变量的值,即使它曾经具有 const 值:

foo = [1, 2, 3]; // 是 const []

您无法更改 const 变量的值:

baz = [42]; // Error: Constant variables can't be assigned a value.

您可以定义使用类型检查和强制转换( is 和 as )、集合 if 和扩展运算符( ... 和 ...?):

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 对象及其字段无法更改:它们是不可变的。

有关使用 const 创建常量值的详细信息,请参阅列表、映射和类。


Comments

Make a comment