Skip to content

Dart中的空安全:防踩坑的利器

写Dart代码的时候,经常会遇到一个烦人的问题:空值(null)。如果一个变量不小心变成了null,程序运行时就会报错,抛出那个让人头疼的“空指针异常”。这在开发圈子里可是个老大难问题。

举个例子:

dart
void main() {
  String name;  // 没给name赋值
  print(name.length); // 编译直接报错
}

上面这段代码,name没赋值,试图访问它的.length属性,编译器直接给你一巴掌。为了在代码运行前就抓住这种问题,Dart从2.12版本开始引入了**空安全(Null Safety)**功能。

空安全为啥这么重要?

  • 代码更稳:写代码时就能发现运行时的空值问题,防患于未然。
  • 类型更清晰:声明一个String类型,意思就是这个变量绝对不会是null。
  • 性能更优:编译器知道变量肯定有值,就能做更多优化。

来看个简单的例子:

dart
void main() {
  String name = "Turabi"; // 非空变量,不能是null
  // name = null; // 直接报错,编译器不让
  String? surname = null; // 可空变量,可以是null
  surname = "Sancak"; // 现在赋了个值
}

解释一下:

  • String name → 非空变量,必须一直有值。
  • String? surname → 可空变量,可能有值,也可能是null。

可空和非空变量

空安全的核心就是两个概念:非空变量可空变量

  • 非空变量:值绝对不能是null,Dart默认所有变量都是这种。
  • 可空变量:值可以是null,类型后面得加个?

非空变量例子

dart
void main() {
  String name = "Turabi";
  print(name.toUpperCase()); // 没问题,输出:TURABI
  // name = null; // 报错,非空变量不能赋null
}

name必须得有值,编译器压根不让你赋null。

可空变量例子

dart
void main() {
  String? surname = null; // 可以是null
  print(surname); // 输出:null
  surname = "Sane";
  print(surname); // 输出:Sane
}

这里用String?,说明surname可以是字符串,也可以是null。

现实中空安全用在哪儿?

  • 可选信息:比如用户不一定填姓氏,用String? surname
  • 外部数据:API返回的字段可能为空,比如int? age
  • 延迟赋值:变量先是null,后续再赋值。

空值检查的几种方法

如果变量是可空的(String?),操作时得小心,不然可能引发运行时错误。Dart提供了几种检查空值的方法,帮你写出更安全的代码:

1. ?. — 空安全访问(稳妥操作)

如果变量不是null,就执行右边的操作;如果是null,直接返回null。

dart
void main() {
  String? name = "Leroy";
  print(name?.toUpperCase()); // 输出:LEROY
  name = null;
  print(name?.toUpperCase()); // 输出:null
}

为啥用它?

  • 变量可能为null,但你只想在它有值时执行操作。
  • 遇到null不会报错,直接返回null,稳得一批。

优点

  • 安全,程序不会崩。
  • 代码简洁,name?.toUpperCase()一目了然。

缺点

  • 如果是null,只返回null,没法提供备选值。
  • 如果你不想看到null,得用其他方法。

2. ?? — 空值合并(默认值)

如果变量是null,就用??后面的值。

dart
void main() {
  String? message;
  print(message ?? "没消息"); // 输出:没消息
  message = "你好";
  print(message ?? "没消息"); // 输出:你好
}

这例子展示了Dart空安全怎么通过??操作符明确处理空值,写出安全的代码。

为啥用它?

  • 想在变量为null时给个默认值。
  • 特别适合用户没输入值时,给个“兜底”值。

优点

  • null情况完全在你掌控中。
  • 代码读起来简单。

缺点

  • 得每次都指定默认值。
  • 有时候默认值可能不太合适。

3. ??= — 空值赋值(仅空时赋值)

只有当变量是null时,才会给它赋值。

dart
void main() {
  String? city;
  city ??= "利物浦";
  print(city); // 输出:利物浦
  city ??= "伊斯坦布尔";
  print(city); // 还是输出:利物浦(因为已经不是null,不会变)
}

意思是:“只有它本来是null,才给它赋个初始值。”

为啥用它?

  • 想在变量为null时赋个值,之后就不改了。
  • 特别适合初始化场景。

优点

  • 一行代码搞定检查和赋值。
  • 默认值赋值场景写起来超短。

缺点

  • 用多了可能让代码可读性变差。
  • 适合初始赋值,但不适合后续更新。

4. ! — 空断言(强制使用)

你跟编译器说:“我打包票,这变量肯定不是null!”如果猜错了,运行时会报错。

dart
void main() {
  String? name = "Teoman";
  print(name!.toUpperCase()); // 输出:TEOMAN
  name = null;
  print(name!.toUpperCase()); // 报错:用了个null值
}

重要提醒:这是最后手段!先试试其他安全方法。

为啥用它?

  • 编译器觉得变量可能为null,但你确信它不是。

优点

  • 不用写多余的if??,直接用。

缺点

  • 猜错了就崩,程序直接挂。
  • 得100%确定才敢用。

5. 用if检查空值

最经典的方法就是用if判断。如果变量不是null,Dart的编译器会在if块里把变量当非空处理,这叫流分析

dart
void main() {
  String? name;
  if (name != null) {
    // 在这块儿,name被当做非空的String
    print(name.toUpperCase());
  } else {
    print("name是null");
  }
}

编译器看到if (name != null),就不需要用!强制断言。

为啥用它?

  • 最传统、最易读的方法。
  • 复杂场景(要执行多个操作)时,if更清晰。

优点

  • 编译器通过流分析自动把变量当非空处理。
  • 代码逻辑一目了然。

缺点

  • 简单场景下,可能会觉得代码有点长。

late关键字的用法

在Dart里,声明非空变量时,编译器会要求你立刻赋值。但有时候,值得晚点才能确定,这时候就得用late关键字。

1. late是啥?

  • late意思是:“我现在不给这变量赋值,但保证用之前一定赋上值。”
  • 你跟编译器打包票:“放心,我会搞定的。”

2. 不用late会咋样?

dart
void main() {
  String token; // 非空变量
  print(token); // 报错:非空变量得先赋值才能用
}

没赋值就用,编译器直接报错。

3. 用late解决问题

dart
void main() {
  late String token; // 现在是空的,晚点赋值
  token = "gs1905"; // 赋了个值
  print(token); // 输出:gs1905
}

用了late,你告诉Dart:“我稍后会赋值”,编译器就放过你了。

4. late的坑

如果你没兑现承诺,忘了给late变量赋值:

dart
void main() {
  late String token;
  print(token); // 运行时错误:LateInitializationError
}

这时候程序会直接崩。

5. 啥时候用late

适合的场景

  • 异步操作:比如从API拿数据后再赋值。
  • Flutter控件生命周期:在initState里给控制器赋值。
  • 依赖注入:保证晚点会提供值的情况。

不适合的场景

  • 值真的可能是可选的(用String?更好)。
  • 有可能一直是null的情况。

6. late vs String?

dart
// late
late String name; // 意思是:这变量不会是null,但我晚点赋值。

// nullable
String? surname; // 意思是:这变量可以是null,也可以有值。

区别

  • late:你保证它不会是null。
  • ?:你允许它可能是null。

总结

空安全是Dart的一大杀器,能大大降低现代应用的错误率。它把变量是否可能为null直接融入类型系统,让你在编译和运行时都更安全。

搞清楚StringString?的区别,合理用?.????=!if这些工具,能让你的代码更健壮、更易读。尤其在大项目里,空安全能帮你防住很多意外崩溃。

记住,空安全其实就是在回答这个问题:
“这个变量是一直有值,还是可能为null?”

只要你问对了这个问题,也答对了,空安全就不会是啥复杂问题,反而会是你写出安全、干净代码的得力助手。