前言
- 学习之路需深耕细作,切勿轻视任一知识点,因其存在必蕴含深意
在软件开发中,空值(null)的处理一直是一个令人头疼的问题。空指针异常不仅会导致程序崩溃,还会增加调试和维护的难度。Dart 2.12引入了空安全(Sound Null Safety)特性,这一特性旨在帮助开发者检测并避免使用空值,从而提高代码的健壮性和可靠性。本文将详细介绍Dart中空安全的基本概念、使用方法以及它如何改变我们的编码习惯。
基本概念
空安全的意思是每个变量都不能为空或提前明确声明其可为空。在Dart中,通过以下方式声明变量的可空性:
- 非空变量:默认情况下,Dart认为所有变量都是非空的。例如,
String name = "Alice";表示name是一个非空字符串。 - 可空变量:在变量类型后添加
?表示该变量可以为空。例如,String? nullableName;表示nullableName是一个可以为空的字符串。
使用方法
1. 空值合并运算符 ??
空值合并运算符??用于检查一个表达式是否为null。如果表达式的结果不是null,则返回该结果;如果结果是null,则返回一个指定的默认值。
String? a;
String b = a ?? "猜猜我是谁"; // 如果 a 是 null,则 “猜猜我是谁”被赋值为 b 的值;否则,a 保持不变
print(b);//输出:猜猜我是谁
2. 空感知赋值运算符??=
??= 运算符检查其左侧的操作数是否为 null。如果左侧的操作数不是 null,则不会改变该变量的值;如果左侧的操作数是 null,则将该变量赋值为右侧操作数的值。
String a;
String b = "我是b";
a ??= b; // 如果 a 是 null,则 a 被赋值为 b 的值;否则,a 保持不变
print(a); // 输出:我是b
3.空全调用运算符?.
如果?.调用的对象是null,则整个表达式的结果为null,而不会抛出空指针异常。
String? nullableName; // 如果 nullableName 为 null,则 length 也为 null
int? length = nullableName?.length;
print(length); // 输出: null
4.非空断言!
当确定一个可空变量在某个时刻不为空时,可以使用非空断言!来告诉编译器。
String? string;
void main() {
string = "Hello, World!";
print(string!.length); // 输出13
}
5. 使用late关键字延迟变量初始化
当变量的值在后续代码中才能确定时,可以使用late关键字延迟初始化。
late String lateString;
void main() {
lateString = "Hello, Dart!";
print(lateString);、//输出 Hello, Dart!
}
6.required 修饰符
使用 required 修饰的参数,在函数调用时必须提供值,且该值不能为空。这有助于避免在函数体内部处理空值的情况,从而提高代码的健壮性和可读性。
void printPersonInfo({required String name, required int age}) {
print('Name: $name, Age: $age');
}
// 正确的调用方式
printPersonInfo(name: 'Alice', age: 30);
// 错误的调用方式(会导致编译错误,因为缺少 required 参数)
// printPersonInfo();
// printPersonInfo(age: 30);
优点
- 在引入空安全之前,Dart的类型系统中,空值(null)被视为所有类型的子类。这意味着任何类型的变量都可以接受一个null值。(如下图)
- 引入空安全后,Dart将空值(null)独立出来,不再视为所有类型的子类。这意味着除了特殊的Null类型允许传递null值外,其他类型均不允许接受null值。(如下图)
1.提高代码的健壮性
- 空安全允许开发者明确区分可为空的类型和不可为空的类型,这有助于在编译阶段就捕获潜在的空引用错误,从而避免这些错误在运行时导致程序崩溃。
2.增强代码的可读性和可维护性
- 使用空安全特性,开发者需要在代码中显式地声明变量的可空性或不可为空性,这使得代码更加清晰易懂。
3. 提高代码质量
- 明确的空值处理规则使得代码逻辑更加清晰,减少了不必要的
if (x != null)检查。