Dart变量
Dart中的变量声明 变量默认值 Final和Const
初始化
创建一个变量并进行初始化:
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类型的构造函数,这种类型的构造函数创建的对象是不可改变的。任何变量都可以有一个不变的值