空安全
空安全是 Dart 最重要的特性之一,在 Dart 2.12 中引入。它有助于防止空引用错误,这是最常见的编程错误之一。
理解空安全
在具有空安全的 Dart 中,变量默认是非空的。这意味着除非明确声明为可空,否则它们不能包含 null。
dart
// 非空(默认)
String name = 'Alice';
// name = null; // 错误:不能将 null 赋给非空变量
// 可空(使用 ?)
String? nullableName = 'Bob';
nullableName = null; // 可以可空类型
在类型后添加 ? 使其可空:
dart
int? age; // 可以是 int 或 null
String? name; // 可以是 String 或 null
List<int>? numbers; // 可以是 List<int> 或 null非空类型
没有 ?,变量必须有值:
dart
int count = 0; // 必须初始化
String message; // 错误:必须初始化
void main() {
message = '你好'; // 初始化后可以
}空值感知操作符
空值合并操作符(??)
如果表达式为空,则提供默认值:
dart
String? name;
String displayName = name ?? '访客';
print(displayName); // 访客
int? score;
int finalScore = score ?? 0;
print(finalScore); // 0空值感知赋值(??=)
仅在变量为空时赋值:
dart
String? name;
name ??= '默认';
print(name); // 默认
name ??= '另一个';
print(name); // 默认(未更改)空值感知访问(?.)
安全访问属性或方法:
dart
class Person {
String? name;
int? age;
}
Person? person;
// 安全访问
print(person?.name); // null(无错误)
print(person?.age); // null
person = Person();
person.name = 'Alice';
print(person?.name); // Alice空值断言操作符(!)
断言值不为空:
dart
String? nullable = '你好';
String nonNull = nullable!; // 断言非空
print(nonNull); // 你好
// 如果为空则抛出错误
String? nullValue;
// String error = nullValue!; // 运行时错误!⚠️ 警告:仅在绝对确定值不为空时使用 !。
Late 变量
对稍后初始化但在使用前的变量使用 late:
dart
late String description;
void initialize() {
description = '已初始化';
}
void main() {
initialize();
print(description); // 可以
}
// 带初始化器的 late(延迟初始化)
late String heavyComputation = expensiveOperation();
String expensiveOperation() {
print('计算中...');
return '结果';
}
void main() {
print('之前');
print(heavyComputation); // 计算中... 结果
print(heavyComputation); // 结果(不再计算)
}函数中的空安全
返回类型
dart
// 非空返回
String getName() {
return 'Alice';
// return null; // 错误
}
// 可空返回
String? findUser(int id) {
if (id == 1) {
return 'Alice';
}
return null; // 可以
}参数
dart
// 非空参数
void greet(String name) {
print('你好,$name');
}
// 可空参数
void greetOptional(String? name) {
print('你好,${name ?? '访客'}');
}
void main() {
greet('Alice');
// greet(null); // 错误
greetOptional('Bob');
greetOptional(null); // 可以
}类型提升
Dart 在空值检查后自动将可空类型提升为非空:
dart
String? name = 'Alice';
if (name != null) {
// name 被提升为 String(非空)
print(name.length); // 可以,不需要 ?
}
// 使用 is 检查
Object? value = '你好';
if (value is String) {
print(value.length); // value 提升为 String
}集合的空安全
dart
// 非空列表
List<String> names = ['Alice', 'Bob'];
// names.add(null); // 错误
// 可空元素
List<String?> nullableNames = ['Alice', null, 'Bob'];
nullableNames.add(null); // 可以
// 可空列表
List<String>? maybeList;
print(maybeList?.length); // null
maybeList = ['Alice'];
print(maybeList?.length); // 1处理可空值
模式 1:提供默认值
dart
String? input;
String message = input ?? '无输入';模式 2:空值检查
dart
String? name;
if (name != null) {
print(name.toUpperCase());
}模式 3:安全调用
dart
String? name;
print(name?.toUpperCase()); // 如果 name 为空则为 null模式 4:断言非空
dart
String? name = getName();
print(name!.toUpperCase()); // 仅在确定不为空时使用空安全最佳实践
- 尽可能优先使用非空类型
- 谨慎使用
late- 仅在必要时使用 - 避免使用
!除非你绝对确定 - 使用
?.进行安全导航 - 使用
??提供默认值 - 尽可能在声明时初始化变量
- 使用类型提升而不是重复的空值检查
迁移到空安全
如果迁移旧代码:
dart
// 空安全之前
String name;
name = null; // 以前可以
// 空安全之后
String? name; // 必须显式声明为可空
name = null; // 现在可以完整示例
dart
class User {
final String id;
final String name;
String? email;
int? age;
User({
required this.id,
required this.name,
this.email,
this.age,
});
String getDisplayName() {
return name;
}
String? getEmail() {
return email;
}
String getEmailOrDefault() {
return email ?? 'no-email@example.com';
}
void printInfo() {
print('ID:$id');
print('姓名:$name');
print('邮箱:${email ?? '未提供'}');
print('年龄:${age ?? '未提供'}');
// 安全导航
print('邮箱长度:${email?.length ?? 0}');
// 类型提升
if (age != null) {
print('5 年后的年龄:${age + 5}');
}
}
}
void main() {
// 必须提供非空字段
User user1 = User(
id: '1',
name: 'Alice',
email: 'alice@example.com',
age: 25,
);
user1.printInfo();
// 可以省略可空字段
User user2 = User(
id: '2',
name: 'Bob',
);
user2.printInfo();
// 空值感知操作
String? searchQuery;
String query = searchQuery ?? '默认';
print('搜索查询:$query');
// Late 变量
late String config;
config = '已加载配置';
print(config);
}