Flutter 空安全精講

前言

看完本章節(jié)你將知道:

  • 什么是空安全
  • 空安全的原則
  • 如何啟用空安全
  • 空安全的類型
  • 空斷言運算符
  • late修飾符

視頻教程

空安全視頻教程地址

空安全介紹

空安全null-safe type system是在Dart 2.12中引入的次伶,如果開啟空安全硬猫,默認(rèn)情況下代碼中的類型不能為空责蝠,也就是說除非聲明該類型是可以為空的,否則值不能為空。

空安全是官方極力推薦的,現(xiàn)在很多流行的第三方庫全部都是支持了空安全庞钢,所以空安全是我們必須要掌握的知識。

空安全的原則

  • 默認(rèn)不可空:除非你將變量顯式聲明為可空因谎,否則它一定是非空的類型基括。
  • 漸進遷移:可以自由地選擇何時進行遷移,多少代碼會進行遷移财岔,還可以使用混合模式的空安全风皿,在一個項目中同時使用空安全和非空安全的代碼。
  • 完全可靠:對代碼的健全性帶來的所有優(yōu)勢——更少的 BUG匠璧、更小的二進制文件以及更快的執(zhí)行速度揪阶。

啟用空安全

空安全在Dart 2.12 和 Flutter 2.0中可用,可通過指定Dart SDK版本為2.12那么就會開啟空安全

environment:
  sdk: ">=2.12.0 <3.0.0"

空安全類型

空安全分可為空和不可為空患朱,可為空就是變量鲁僚、形參都可以傳null值,不可為空變量裁厅、形參一定不能為空冰沙,我們在使用空安全的時候會碰到下面三種情況,接下來的代碼演示我們都是dart 2.12開啟空安全為準(zhǔn)

  • 變量為空編譯時報錯
  • 傳遞參數(shù)時為空編譯時報錯
  • 方法需要返回參數(shù)時必須返回执虹,否則編譯時報錯

變量可為空和不可為空的使用對比

聲明一個空變量

這里我聲明了一個變量為name的字符串屬性拓挥,但并沒有賦值,所以name的內(nèi)存地址存的是一個空的字符串袋励。

String name;

錯誤提示

它提示說不可為空的變量一定要進行初始化

[圖片上傳失敗...(image-30e8ab-1632465574247)]

標(biāo)明變量可為空

我們可以在Stirng后面加一個?號侥啤,該符號標(biāo)明name這個變量可以為空当叭,這個時候我們發(fā)現(xiàn)定義時不會出現(xiàn)報錯,但是我們在使用name屬性的時候會發(fā)現(xiàn)有一個報錯盖灸,它報錯的信息是String?不能分配給一個String蚁鳖,如下:

[圖片上傳失敗...(image-da9c52-1632465574247)]

空斷言運算符

在上面我們使用name這個屬性的時候會出現(xiàn)一個報錯,我們可以使用空斷言運算符!來標(biāo)明該值不會為空赁炎,所以Dart在編譯時不會報錯醉箕,該符號在項目中盡量不要使用,除非你明確知道它是不為空的徙垫,因為我們name屬性還是空的讥裤,所以在運行時將會收到如下報錯:

======== Exception caught by widgets library =======================================================
The following _CastError was thrown building MyHomePage(dirty, state: _MyHomePageState#5824d):
Null check operator used on a null value

The relevant error-causing widget was: 
  MyHomePage MyHomePage:file:///Users/jm/Desktop/Work/Git/my_project/flutter_null_safety/lib/main.dart:29:13
When the exception was thrown, this was the stack: 
..........
(package:flutter/src/rendering/binding.dart:319:5)
#123    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1143:15)
#124    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1080:9)
#125    SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:863:7)
(elided 4 frames from class _RawReceivePortImpl, class _Timer, and dart:async-patch)
====================================================================================================

參數(shù)可為空和不可為空的使用對比

聲明一個需要傳參的方法

下面這段代碼我們定義了一個可選參數(shù)為name的字符串,在空安全的機制下姻报,我們必須保證傳入的參數(shù)不能為空己英,那么將會收到如下報錯:

_upperCase({String name}) {
  setState(() {
    value.toUpperCase();
  });
}

floatingActionButton: FloatingActionButton(
  onPressed: _upperCase(),
  child: Icon(Icons.add),
), 

錯誤提示

它提示參數(shù)name的類型不能為null值,但是被隱式轉(zhuǎn)換成了null吴旋,我們碰到這種情況有兩種解決方案损肛,我們接下來看看如果解決

[圖片上傳失敗...(image-d2dd46-1632465574247)]

解決方法

第一種:給可選參數(shù)添加默認(rèn)值

_upperCase({String name = "Jimi"}) {
  setState(() {
    name.toUpperCase();
  });
}

第二種:給可選參數(shù)增加required

_upperCase({required String name}) {
  setState(() {
    name.toUpperCase();
  });
}

當(dāng)給可選參數(shù)增加required后,說明可選參數(shù)name必傳邮府,這樣會導(dǎo)致調(diào)用處會報錯,我們來在調(diào)用時把name傳進去溉奕,如下:

floatingActionButton: FloatingActionButton(
  onPressed: _upperCase(name: name!),
  child: Icon(Icons.add),
), 

這里使用name!來進行傳入褂傀,在上面我們說過這個是空斷言運算符,只有你明確知道該變量會有值時才使用加勤,那么我們這里這樣使用還是會報錯仙辟,所以我們來繼續(xù)優(yōu)化,把name聲明一個有值的變量鳄梅,如下:

String name = "Jimi";

floatingActionButton: FloatingActionButton(
  onPressed: _upperCase(name: name),
  child: Icon(Icons.add),
), 

方法的返回值可為空和不可為空的使用對比

聲明一個需要有返回值的方法

下面我聲明了一個方法用來將name`的轉(zhuǎn)換為大寫叠国,在我們沒有引入空安全之前下來代碼是不會報錯的,但是引入空安全機制后你會收到如下報錯:

String name = "Jimi";

String _upperCaseName(String name) {
}

錯誤提示

它提示返回類型可能是不能為null的類型戴尸,可以嘗試在最后添加 returnthrow語句粟焊。

[圖片上傳失敗...(image-1eb4c4-1632465574247)]

解決方法

第一種:添加return語句

String _upperCaseName(String name) {
  return name.toUpperCase();
}

第二種:添加throw語句

String _upperCaseName(String name) {
  throw "no return";
}

自定義類字段可為空和不可為空使用對比

聲明一個User類

我們在平時的開發(fā)過程中常常會自定義類蜜自,而自定義類中的屬性一開始都不會賦值热凹,那么我們引入空安全的情況下將會報錯,如下所示:

class User {
  String name;
  int age;

  setName(String name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }

  setAge(int age) {
    this.age = age;
  }

  getAge() {
    return this.age;
  }
}

報錯提示

它這里提示name以及age必須要有初始化值蚀浆,或者標(biāo)記為late挎峦。

[圖片上傳失敗...(image-17a779-1632465574247)]

解決方法

第一種:聲明變量可為空

我們采用聲明變量可為空的方式香追,在使用的時候我們使用空斷言運算符!來進行寫入和讀取操作,但是這也暗示著null對于字段來說是有用的值坦胶,這樣就背道而馳了透典,所以建議不要采用這種方式晴楔,接下來我們來看看第二種解決方案late修飾符。

String? name;
int? age;

第二種:late修飾符

因為late涉及內(nèi)容較多峭咒,我們拿一個章節(jié)來講解税弃。

late修飾符

late修飾符是在運行時而非編譯時對變量進行約束,這也就是說late相當(dāng)于何時執(zhí)行對變量的強制約束讹语。

比如本示例中name字段用late修飾后并不一定已經(jīng)被初始化钙皮,每次它被讀取時,都會插入一個運行時的檢查顽决,以確保它已經(jīng)被賦值短条。如果并未賦值,就會拋出一個異常才菠,給變量加上String類型就是說:“我的值絕對是字符串”茸时,而加上late修飾符意味著:"每次運行我都要檢查是不是真的"。

當(dāng)使用late修飾符總結(jié)如下:

  • 先不給變量賦值
  • 稍后再給變量賦值
  • 在使用前會給變量賦值
  • 在使用前不賦值赋访,將會報錯
late String name;
late int age;

late修飾符懶加載

懶加載也有一種說法是初始化延遲執(zhí)行可都,當(dāng)你用late修飾變量后,那么它將會被延遲到字段首次被訪問時才會執(zhí)行蚓耽,而不是在實例化構(gòu)造器時就初始化了渠牲。而且實例字段的初始化內(nèi)容是無法訪問this的,因為在所有的初始化方法完成前步悠,是無法訪問到新的實例對象签杈。但是,使用了late的話就可以訪問到this鼎兽、調(diào)用方法以及訪問實例的字段答姥。

不使用late修飾符

我們這里就是創(chuàng)建一個User類,name屬性直接調(diào)用getUserName()方法谚咬,最后當(dāng)我們實例化一個User對象并獲取name的值鹦付,我們來看下控制臺輸出:

void main() {
  print("調(diào)用構(gòu)造函數(shù)");
  var user = User();
  print("獲取值");
  print("獲取的值為: ${user.getName()}");

}

class User {
  String name = getUserName();

  setName(String name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

String getUserName() {
  print("返回用戶的名稱");
  return "Jimi";
}

控制臺輸出

flutter: 調(diào)用構(gòu)造函數(shù)
flutter: 返回用戶的名稱
flutter: 獲取值
flutter: 獲取的值為: Jimi

使用late修飾符

代碼和上面一樣,只是在定義字段處加了一個late修飾符择卦,我們來看一下控制臺輸出:

void main() {
  print("調(diào)用構(gòu)造函數(shù)");
  var user = User();
  print("獲取值");
  print("獲取的值為: ${user.getName()}");

}

class User {
  late String name = getUserName();

  setName(String name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

String getUserName() {
  print("返回用戶的名稱");
  return "Jimi";
}

控制臺輸出

flutter: 調(diào)用構(gòu)造函數(shù)
flutter: 獲取值
flutter: 返回用戶的名稱
flutter: 獲取的值為: Jimi

我們明顯可以看到第二行和第三行反過來了敲长,而在不使用late修飾符也就是沒有懶加載的情況下,當(dāng)我們實例化構(gòu)造器時就直接調(diào)用了獲取值的方法秉继。而加了late修飾符后是在使用該字段的時候才會去進行獲取潘明。

總結(jié)

空安全的引入讓我的代碼變得更加可靠

  • 在類型上都必須是非空的,當(dāng)然你也可以添加?變成可空的秕噪,用空斷言運算符!進行使用钳降。
  • 可選參數(shù)都必須是非空的,可以使用required來構(gòu)建一個非可選命名參數(shù)腌巾。
  • List 類現(xiàn)在不再允許包含未初始化的元素遂填。
  • late修飾符在運行時檢查铲觉,能夠使用非空類型和 final,它同時提供了對字段延遲初始化的支持吓坚。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撵幽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子礁击,更是在濱河造成了極大的恐慌盐杂,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哆窿,死亡現(xiàn)場離奇詭異链烈,居然都是意外死亡,警方通過查閱死者的電腦和手機挚躯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門强衡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人码荔,你說我怎么就攤上這事漩勤。” “怎么了缩搅?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵越败,是天一觀的道長。 經(jīng)常有香客問我硼瓣,道長究飞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任巨双,我火速辦了婚禮噪猾,結(jié)果婚禮上霉祸,老公的妹妹穿的比我還像新娘筑累。我一直安慰自己,他們只是感情好丝蹭,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布慢宗。 她就那樣靜靜地躺著,像睡著了一般奔穿。 火紅的嫁衣襯著肌膚如雪镜沽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天贱田,我揣著相機與錄音缅茉,去河邊找鬼。 笑死男摧,一個胖子當(dāng)著我的面吹牛蔬墩,可吹牛的內(nèi)容都是我干的译打。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼拇颅,長吁一口氣:“原來是場噩夢啊……” “哼奏司!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起樟插,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤韵洋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后黄锤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搪缨,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年猜扮,在試婚紗的時候發(fā)現(xiàn)自己被綠了勉吻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡旅赢,死狀恐怖齿桃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情煮盼,我是刑警寧澤短纵,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站僵控,受9級特大地震影響香到,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜报破,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一悠就、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧充易,春花似錦梗脾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稿静,卻和暖如春梭冠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背改备。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工控漠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悬钳。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓盐捷,卻偏偏與公主長得像柬脸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子毙驯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容