Skip to content

空安全

空安全是 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());  // 仅在确定不为空时使用

空安全最佳实践

  1. 尽可能优先使用非空类型
  2. 谨慎使用 late - 仅在必要时使用
  3. 避免使用 ! 除非你绝对确定
  4. 使用 ?. 进行安全导航
  5. 使用 ?? 提供默认值
  6. 尽可能在声明时初始化变量
  7. 使用类型提升而不是重复的空值检查

迁移到空安全

如果迁移旧代码:

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);
}

下一步