# Dart教程 - 4 非空安全

null 值是一个特殊的值,表示一个变量没有被初始化或者没有值。

举个栗子:

void main() {
  var a;					// 只是声明,没有初始化
  print(a);				// 输出:null
}
1
2
3
4

非空安全类型系统是 Dart 2.12 引入的一项重要功能,它可以在编译时检查代码,以确保不会出现空引用错误。在Dart非空安全类型系统中,类型被分为可空类型非空类型

# 4.1 非空类型

默认情况下,所有类型都是非空类型,因此变量、函数参数或返回值不能为 null

举个栗子:

我们编写下面的代码编译是会报错的:

void main() {
  int age = null;		// 报错,无法附空值

  String name;
  name = null;			// 报错,无法附空值
}
1
2
3
4
5
6

上面的代码放在Dart 2.12之前的版本是可以的。

所以我们使用非空类型的时候,在使用前必须进行赋值:

void main() {
  String name;
  // print(name);      // 报错,使用前必须赋值
  name = 'John Doe';
  print(name);
}
1
2
3
4
5
6

# 4.2 可空类型

在Dart中,使用?来标记一个变量为可空类型。可空类型表示这个变量可以是一个非空值或者空值。

举个栗子:

String? name = null;		// 声明name为可空类型,可以赋值null
name = "John Doe";
String firstName = name.split(' ')[0];
print(firstName);
1
2
3
4

在上面的代码中,name变量被标记为可空类型。它可以是一个非空字符串或者空值。

# 4.3 类型提升

为了保证空安全特性,Dart 的流分析(flow analysis)已经考虑了空特性。如果一个可空对象不可能有空值,那么就会被当作非空对象处理。

举个栗子:

下面的代码编译是会报错的。

String? str;
print(str.length); // 编译出错,str为null
1
2

但是如果我们对str进行非空判断,则确保了str不会为空,编译就不会报错了:

String? str;
if (str != null) {
  print(str.length); // 已经确保 str 不为空,不会编译出错
}
1
2
3
4

# 4.4 非空断言

在Dart中,使用 ! 来进行非空断言。非空断言表示这个变量一定不是空值。

举个栗子:

我们在调用tryParse()方法,将字符串转换为整形数字的实现:

String str = "123";
int? num = int.tryParse(str);
int abc = num!;
print(abc);
1
2
3
4

因为tryParse()方法返回的是可空类型的,所以我们可以使用非空断言,告诉编译器num变量一定不为空,然后将num赋值给abc。

在使用非空断言的时候,一定要确保变量一定不为空,如果它是空值则会抛出异常。

# 4.5 空安全调用

在Dart中,使用 ?. 来进行空安全调用。空安全调用表示如果这个变量是空值,则不会调用它的函数或者访问它的属性。

String? str = null;
print(str?.length);		// 输出:null
1
2

在上面的代码中,虽然 str 为null,但是使用了空安全调用,所以不会报错。

# 4.6 空值合并运算??

空值合并运算符用于判断一个表达式是否为null,如果是null则返回一个默认值,否则返回这个表达式的值。

举个栗子:

String? name;
String defaultName = 'John Doe';
String fullName = name ?? defaultName;
print(fullName); // 输出:John Doe
1
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
1
2
3

在上面的代码中,如果变量 bnull,则将其赋值为 10。如果变量 b 已经有了一个非 null 的值,那么 ??= 运算符就不会进行任何操作。