【老孟Flutter】強(qiáng)大的空安全

簡(jiǎn)介
空安全(Sound null safety)是 Dart 2.12 中新增的一項(xiàng)特性,空安全特性并不是 Dart 獨(dú)有的,Kotlin, TypeScript, C#, Swift 等語言都有此特性,如果你了解這些語言的空安全特性及用法,那么下面關(guān)于 Dart 語言空安全特性的介紹你會(huì)感到非常熟悉,因?yàn)?Dart 語言空安全和其他語言基本一致。
版本要求
Dart 2.12和Flutter 2中提供了空安全性,對(duì)應(yīng)到Flutter項(xiàng)目中,則需要在pubspec.yaml文件中添加如下配置:
environment:
sdk: ">=2.12.0 <3.0.0"
基本使用
變量
定一個(gè) int 類型的變量,
int age = null;
在沒有空安全前,上面的代碼是沒有問題的,但當(dāng)使用空安全后,在編譯階段出現(xiàn)異常,如下:

異常提示:null不能賦值給int變量。
這是空安全與以前最大的不同,默認(rèn)情況下,變量不能為null(空安全以前任何類型都可以設(shè)置為null),更重要的是此異常在編譯階段即出現(xiàn)異常,無法編譯通過。
如果想給一個(gè)變量賦值 null 要如何處理?只需在類型后面添加 ? 即可,如下:
int age = 1;
int? ageNull = null;
String? name = null;
類型后面跟操作符 ? 表示當(dāng)前變量可為null。
變量的使用:
String? name = null;
print('name length:${name?.length}');
非常簡(jiǎn)單,輸出 name 字符串的長(zhǎng)度,此時(shí)會(huì)發(fā)現(xiàn),無法編譯通過,異常如下:

修改如下:
String? name = null;
print('name length:${name?.length}');
輸出:
flutter: name length:null
注意:上面 name 為 null,調(diào)用 name?.length 不會(huì)拋出異常,而是返回 null。
還可以有另外一種方式處理上面的異常:使用操作符 !
String? name = null;
print('name length:${name!.length}');
上面的代碼雖然可以編譯通過,但運(yùn)行時(shí)拋出異常,操作符 ! 表示檢測(cè)當(dāng)前變量不為 null,開發(fā)者需要保證變量不為 null,否則會(huì)拋出異常。
如果無法確認(rèn)變量不為null,千萬不要使用操作符 !
集合
看如下List集合:
List<String> list;
List<String>? list1;
List<String?> list2;
List<String?>? list3;
他們的區(qū)別就是是否可為 null 的區(qū)別,List 表示 List 不為 null 而且集合中的 Item 也不能為 null。那么如下代碼就是錯(cuò)誤的:
List<String> list;
//錯(cuò)誤
list = null;
list.add(null);
List 集合說明如下:
| 類型 | 集合是否可為null | Item 是否可以為null |
|---|---|---|
| List | 否 | 否 |
| List | 是 | 否 |
| List<String?> | 否 | 是 |
| List<String?>? | 是 | 是 |
Map 類型也是同理,Map 中的 key 一般不為 null,下面的 Item 指的是Map 中的 value:
| 類型 | 集合是否可為null | Item 是否可以為null |
|---|---|---|
| Map<String,String> | 否 | 否 |
| Map<String,String>? | 是 | 否 |
| Map<String,String?> | 否 | 是 |
| Map<String,String?>? | 是 | 是 |
方法參數(shù)
void _incrementCounter(String? name) {
print('name length:${name?.length}');
}
上面方法參數(shù)中加入了空安全,與變量用法一致。
class
定義一個(gè)類:
class Person{
final String name;
Person(this.name);
}
有一個(gè)屬性 name,屬性類型為 String,說明此屬性不能為 null,下面的使用是錯(cuò)誤的:
//錯(cuò)誤,無法編譯通過
var persion = Person(null);
//正確
var persion1 = Person('123');
將屬性 name 改為可為 null:
class Person{
final String? name;
Person(this.name);
}
那么下面的用法都是正確的:
//正確
var persion = Person(null);
//正確
var persion1 = Person('123');
初始化 late
假設(shè)有一個(gè)屬性,此屬性的值來源于服務(wù)器或者其他方法,那么此時(shí)無法給此屬性進(jìn)行初始化,代碼如下:
String name;
此時(shí)會(huì)編譯異常:

提示我們必須要初始化,此情況使用關(guān)鍵字 late:
late String name;
使用此屬性前 一定 要賦值,下面的用法運(yùn)行時(shí)拋出異常:
late String name;
void _incrementCounter() {
print('name length:${name.length}');
}
異常:

正確用法:
late String name;
void _incrementCounter() {
name = '123';
print('name length:${name.length}');
}
總結(jié)
空安全增加了2個(gè)操作符 ? 和 ! ,1個(gè)關(guān)鍵字 late。
? :放在類型后面表示當(dāng)前變量可為null,例如 int a 和 int? b ,a 不能為null,而 b 可以。
! :放在變量后面,表示此變量值不為null,如果為null則會(huì)拋出異常,此操作符經(jīng)常用于如下場(chǎng)景:一個(gè)方法的參數(shù)為非空類型(int),而傳遞給當(dāng)前方法的變量是可為null的類型(int?),那么此時(shí)編譯出現(xiàn)異常,在類型不變的情況下,在此變量的后面添加 ! ,表示當(dāng)前變量不為null,代碼如下:
int? b = 2;
int _add(int a){
return a+1;
}
//方法調(diào)用
_add(b!);late:表示延遲初始化,通常用于延遲加載(比如網(wǎng)絡(luò)請(qǐng)求),late 聲明的變量在使用前一定要進(jìn)行初始化。

