final
、 const
及wildcard
final
声明为final的变量,只能被赋值一次。 如果尝试修改已经赋值过的final变量,会引起编译错误。
// ch0103_1.dart
void main() {
final myPet = 'Doggy'; // 1
myPet = 'Kitty'; // 2
// Error: Can't assign to the final variable 'myPet'.
// myPet = 'Kitty'; // 2
// ^^^^^
}
- 声明一个final变量
myPet
, 并为其赋值; - 尝试修改
myPet
的值,引起编译错误, 告诉我们不能给final 变量myPet
赋值。
late final
late final
这两个关键字经常一起出现,这里构建了一个简单的例子:
// ch0103_2.dart
void main() {
final width = 3.0; // 1
final height = 4.0; // 1a
late final double perimeter; // 2
// ... (Do somehting else)
perimeter = 2 * (width + height); // 3
print('width: $width, height: $height, perimeter: $perimeter'); // 4
// Output: width: 3.0, height: 4.0, perimeter: 14.0
}
- 声明final变量
width
与height
,并分别赋值为3.0与4.0,Dart自动推断其数据类型为double
(浮点数); - 使用
late final
声明一个变量perimeter
,在这里它表示一个矩形的周长,同时显示指定这个变量的数据类型为double
,所以这里有3个关键字late final double
,它们一起修饰变量perimeter
; - 计算变量
perimeter
的值,公式为(长+宽)×2; - 使用字符串插值,打印相关变量。
这个例子似乎看不出 late final
有什么特别之处,这里只是让你熟悉一下,在面向对象编程(OOP)的章节还会继续讨论它。
final
的兄弟 const
const
关键字用于:
- 声明一个常量,常量具有编译时(compile-time)不可变性,这里的编译时是与运行时(runtime)相对应的;
- 创建常量值(constant values);
- 定义const构造器,该构造器用于创建常量值。
后面两点与OOP有关,这里提前了解下。
声明一个常量
试图修改常量会引起编译错误,例如
// ch0103_3.dart
void main(){
const score = 50; // 1
score = 80; // 2
// Error: Can't assign to the const variable 'score'.
// score = 80; // 2
// ^^^^^
}
创建常量值
任何变量都可以有常量值。
// ch0103_4.dart
void main() {
final luckyNumbers = [5, 6]; // 1
luckyNumbers.add(8); // 2
print('luckyNumbers: $luckyNumbers');
// Output: luckyNumbers: [5, 6, 8]
const myFriends = ['Alice', 'Bob']; // 3
myFriends.add('Charlie'); // 4
// Unhandled exception:
// Unsupported operation: Cannot add to an unmodifiable list;
}
- 声明一个
final
列表luckyNumbers
并对其初始化,包含5、6两个数字; - 向
luckyNumbers
添加数字8,然后打印出来,luckyNumbers 现在包含了5、6、8三个数字; 这说明了虽然final
修饰的luckyNumbers本身(引用)不可更改,但它引用的列表是可以改变的; - 声明一个
const
列表myFriends
,初始值包含"Alice和"Bob"两个字符串; - 向
myFriends
添加一个新元素"Charlie",这引起了一个运行时异常,告诉我们不能向不可变列表添加新的元素,可见const
比final
具有更强的不可变性。
定义const构造器
构造器是OOP里的概念,const构造用于创建常量值。请看下面这个示例:
// ch0103_5.dart
class Pet { // 1
final String name; // 1a
const Pet(this.name); // 2
}
void main() {
var myPet = Pet('Doggy'); // 3
myPet = Pet('Little Doggy'); // 3a
const yourPet = Pet('Kitty'); // 4
print('myPet: ${myPet.name}, yourPet: ${yourPet.name}');
// Output: myPet: Little Doggy, yourPet: Kitty
}
class
关键字用于声明一个名叫Pet
的类 ,类封装了数据和行为(方法)(第4-5章专门讨论OOP); classPet
有一个final成员变量叫做name
;- 这是
Pet
的const构造器; - 通过调用
Pet
的构造器,得到一个Pet
的实例(对象)myPet
,随后又将myPet
指向另一个Pet对象(3a); - 通过调用
Pet
的const构造器,创建Pet的常量对象yourPet
,这行代码等于
const yourPet = const Pet('Kitty'); // 4
在常量上下文中,Pet('Kitty')
之前省略了const
关键字。
常量对象
const Pet('Kitty')
与 Pet('Kitty')
是不一样的,前者创建(实例化)了一个常量对象,而后者创建的是非常量对象(除非是在常量上下文中)。
// ch0103_6.dart
void main() {
const pet1 = Pet('Rabbit'); // 1
var pet2 = const Pet('Rabbit'); // 2
final pet3 = Pet('Rabbit'); // 3
print('${pet1 == pet2}'); // Output: true
print('${pet1 == pet3}'); // Ouptput: false
}
class Pet {
final String name;
const Pet(this.name);
}
这里的 ==
操作符,用于测试左右两边的操作数是否相等。
Wildcard(_
)
自 Dart 3.7 开始,以 _
命名 的变量或参数,是一个通配符(wildcard ),它的值会被丢弃。同一个命名空间里多次声明 _
不会冲突。例如
//ch0103_7.dart
void main() {
var _ = 10; // 1
var _ = 'Hello'; // 2
print('Hello, Dart! $_'); // 3
// Error: Undefined name '_'.
// print('Hello, Dart! $_');
}
上面这段代码比较简单,不过多解释。
通配符出现的地方:
- 变量声明, 如
var _ = 1;
- for 循环变量 如
for (var _ in list) {}
- catch 语句参数, 如
try {
throw 'something bad';
} catch (_) {
print('oops');
}
- 函数参数,如
list.where((_) => true);
- 范型类型及函数类型参数, 如
class T<_> {}
void genericFunction<_>() {}
for循环、函数、catch语句、范型等概念将在后续章节陆续介绍,这里关于通配符有个印象就行。
更多的示例,可查阅官方文档。