# Dart教程 - 4 非空安全
null
值是一个特殊的值,表示一个变量没有被初始化或者没有值。
举个栗子:
void main() {
var a; // 只是声明,没有初始化
print(a); // 输出:null
}
2
3
4
非空安全类型系统是 Dart 2.12 引入的一项重要功能,它可以在编译时检查代码,以确保不会出现空引用错误。在Dart非空安全类型系统中,类型被分为可空类型和非空类型。
# 4.1 非空类型
默认情况下,所有类型都是非空类型,因此变量、函数参数或返回值不能为 null
。
举个栗子:
我们编写下面的代码编译是会报错的:
void main() {
int age = null; // 报错,无法附空值
String name;
name = null; // 报错,无法附空值
}
2
3
4
5
6
上面的代码放在Dart 2.12之前的版本是可以的。
所以我们使用非空类型的时候,在使用前必须进行赋值:
void main() {
String name;
// print(name); // 报错,使用前必须赋值
name = 'John Doe';
print(name);
}
2
3
4
5
6
# 4.2 可空类型
在Dart中,使用?
来标记一个变量为可空类型。可空类型表示这个变量可以是一个非空值或者空值。
举个栗子:
String? name = null; // 声明name为可空类型,可以赋值null
name = "John Doe";
String firstName = name.split(' ')[0];
print(firstName);
2
3
4
在上面的代码中,name
变量被标记为可空类型。它可以是一个非空字符串或者空值。
# 4.3 类型提升
为了保证空安全特性,Dart 的流分析(flow analysis)已经考虑了空特性。如果一个可空对象不可能有空值,那么就会被当作非空对象处理。
举个栗子:
下面的代码编译是会报错的。
String? str;
print(str.length); // 编译出错,str为null
2
但是如果我们对str进行非空判断,则确保了str不会为空,编译就不会报错了:
String? str;
if (str != null) {
print(str.length); // 已经确保 str 不为空,不会编译出错
}
2
3
4
# 4.4 非空断言
在Dart中,使用 !
来进行非空断言。非空断言表示这个变量一定不是空值。
举个栗子:
我们在调用tryParse()方法,将字符串转换为整形数字的实现:
String str = "123";
int? num = int.tryParse(str);
int abc = num!;
print(abc);
2
3
4
因为tryParse()方法返回的是可空类型的,所以我们可以使用非空断言,告诉编译器num变量一定不为空,然后将num赋值给abc。
在使用非空断言的时候,一定要确保变量一定不为空,如果它是空值则会抛出异常。
# 4.5 空安全调用
在Dart中,使用 ?.
来进行空安全调用。空安全调用表示如果这个变量是空值,则不会调用它的函数或者访问它的属性。
String? str = null;
print(str?.length); // 输出:null
2
在上面的代码中,虽然 str 为null,但是使用了空安全调用,所以不会报错。
# 4.6 空值合并运算??
空值合并运算符用于判断一个表达式是否为null
,如果是null
则返回一个默认值,否则返回这个表达式的值。
举个栗子:
String? name;
String defaultName = 'John Doe';
String fullName = name ?? defaultName;
print(fullName); // 输出:John Doe
2
3
4
上面 String fullName = name ?? defaultName;
表示先判断 name 是否为null,如果不为null,则将name的值赋值给fullName,否则将defaultName赋值给fullName。
# 4.7 复合赋值运算符??=
??=
运算符是一个复合赋值运算符,用于给变量赋值,当变量为 null
时才将右侧的值赋给变量;否则,不进行任何操作。
举个栗子:
var b;
b ??= 10; // 如果 b 为 null,则将其赋值为 10
print(b); // 输出: 10
2
3
在上面的代码中,如果变量 b
为 null
,则将其赋值为 10
。如果变量 b
已经有了一个非 null
的值,那么 ??=
运算符就不会进行任何操作。