Dart空安全(Null Safety)

一、空安全简介  

        Dart从2.12版本开始强制要求空安全, Dart 空安全(Null Safety)的核心是杜绝运行时空指针异常,这意味着变量默认不能为null 编译器会强制你处理 “变量可能为 null” 的情况。

        但在实际开发中,我们经常需要处理可能为null的值,Dart提供了一系列操作符来优雅地处理空值,而 ?.??! 等操作符,就是 Dart 提供的 “优雅处理 null” 的语法糖。。


二、核心空安全操作符

1、 ?.:空安全访问符(最常用)

  • 作用:如果对象不为 null,就访问其属性 / 方法;如果为 null,直接返回 null(不会崩溃)。
  • 使用场景:访问可能为 null 的对象的属性 / 方法。

对比示例:

// 定义一个可为空的对象
String? name = null;

// ❌ 直接访问:编译报错(name 可能为 null)
print(name.length); 

// ✅ 使用 ?. 访问:安全,返回 null
print(name?.length); // 输出 null

// ✅ 非 null 时正常访问
name = "张三";
print(name?.length); // 输出 2

进阶场景:链式调用

class User {
  String? nickname;
  int? age;
}

User? user = null;
// 多层访问:只要其中一环为 null,整体返回 null
print(user?.nickname?.length); // 输出 null

user = User()..nickname = "李四";
print(user?.nickname?.length); // 输出 2

2、 ??:空合并运算符

  • 作用:如果左边的值为 null,就返回右边的默认值;如果不为 null,返回左边的值。
  • 使用场景:给可为空变量设置 “兜底默认值”。

基础用法:

String? username = null;
// 如果 username 为 null,用 "匿名用户" 替代
String finalName = username ?? "匿名用户";
print(finalName); // 输出 匿名用户

username = "王五";
finalName = username ?? "匿名用户";
print(finalName); // 输出 王五

结合 ?. 使用(高频组合):

User? user = null;
// 先通过 ?. 安全访问,再通过 ?? 设置默认值
String nickname = user?.nickname ?? "未设置昵称";
print(nickname); // 输出 未设置昵称

3、??=:空赋值运算符

  • 作用:如果变量为 null,就给它赋值;如果不为 null,保持原值(相当于 变量 = 变量 ?? 新值
  • 使用场景:给可为空变量 “懒加载” 赋值(仅当变量为 null 时赋值)。
String? title = null;
// title 为 null,赋值为 "默认标题"
title ??= "默认标题";
print(title); // 输出 默认标题

// title 不为 null,不赋值
title ??= "新标题";
print(title); // 输出 默认标题

4. !:空断言运算符(慎用)

  • 作用:告诉编译器 “我确定这个变量不是 null,出问题我负责”,强制将可为空类型转为非空类型。
正确用法(先判空,再断言):
String? content = "Hello";

// 先判空,确保 content 不为 null
if (content != null) {
  // 此时断言是安全的
  print(content!.length); // 输出 5
}

        使用场景:你100% 确定变量不为 null,但编译器无法推断的场景(比如已通过业务逻辑判空)。实例:

// 定义一个可为空的列表(接口返回,业务上确保非空)
List<String>? fruits = ["苹果", "香蕉", "橙子"];

// 业务逻辑:先判空,再取第一个元素
if (fruits != null && fruits.isNotEmpty) {
  // ❌ 编译器报错:fruits 是 List<String>? 类型,即使判空,编译器仍认为可能为 null
  //  String firstFruit = fruits[0]; 
  
  // ✅ 用 ! 告诉编译器:我确定 fruits 不是 null,你放心用
  String firstFruit = fruits![0]; 
}

为什么报错?

  1. 你通过 if (fruits != null && fruits.isNotEmpty) 确认了列表非空,但 Dart 编译器的静态分析能力有限,无法识别这种 “复合判空逻辑”,依然把 fruits 当作 List<String>? 对待。
  2. 此时 fruits[0] 要求 fruits 是非空的 List<String>,类型不匹配,编译失败。

⚠️ 避坑点:除非你能 100% 保证变量非 null,否则不要用 !,优先用 ?./??

5、 ..:级联运算符(和空安全结合使用)

  • 作用:对同一个对象连续调用属性 / 方法(无需重复写对象名),结合 ?. 可实现 “空安全的级联调用”。
  • 使用场景:初始化对象、连续调用多个方法时。

基础级联(非空对象):

不用 ..:重复写对象名,代码冗余

void main() {
  // 初始化对象
  User user = User();
  // 逐个赋值/调用方法(重复写 user.)
  user.name = "张三";
  user.age = 20;
  user.address = "北京市";
  user.sayHello();
}

        用 ..:一次写对象名,连续操作

void main() {
  User user = User()
    ..name = "张三"   // 连续赋值属性
    ..age = 20
    ..address = "北京市"
    ..sayHello();     // 连续调用方法
}

        安全写法(用 ?. + ..,对象为 null 不执行,会跳过级联操作)

void main() {
  User? user = null; // 对象为 null

  // ❌ 崩溃:null 上调用级联运算符
  // user
  //   ..name = "张三"
  //   ..age = 20;
  
  // ✅ 安全:user 为 null,级联操作不执行,无崩溃
  user?.   // 先判断 user 是否为 null
    ..name = "张三"
    ..age = 20;

  // 验证:user 还是 null
  print(user?.name); // 输出 null
}

// 非 null 时正常执行
User? user2 = User();
user2?.
  ..name = "李四"
  ..age = 25;
print(user2?.name); // 输出 李四

        Dart 支持更简洁的 ?..,效果和 ?... 完全一样:

user?..name = "张三"
   ..age = 20;

三、避坑总结(关键)

操作符核心用途注意事项
?.安全访问属性 / 方法避免直接访问可为空对象的成员
??设置默认值右边必须是非 null 的同类型值
!强制非空断言仅在 100% 确定非 null 时用,否则必崩溃
??=懒加载赋值仅变量为 null 时赋值,适合初始化场景
..级联调用结合 ?. 使用,避免空对象级联调用

总结

  1. 优先用 ?. + ??:这是处理空安全最安全、最常用的组合,能覆盖绝大多数场景
  2. 慎用 !:只有通过业务逻辑(如 if (x != null))确认变量非 null 时,才用 ! 断言
  3. 避免 “绕过” 空安全:不要为了省事就用 ! 强行断言,空安全的核心是 “提前处理 null”,而非 “无视 null”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值