iT邦幫忙

2021 iThome 鐵人賽

DAY 12
0
Modern Web

Flutter web 的奇妙冒險系列 第 12

Day 12 | Dart 中的 Sound null safety

為什麼我們需要 null safety?

回答這個問題前應該要先了解為什麼會有 null ,如果寫過網頁前端的讀者應該很常看到以下程式碼(以react舉例)


if(!apiData){
	return <></>
}

其實大部分原因都是因為「我們不確定什麼時候有值」。最常見大概就是來自後端的資料,至於為什麼這樣會產生null ,正如前幾篇文章說過非同步要等待一段時間才會回傳結果,但為了執行緒不阻塞所以執行緒會繼續往下執行程式。

所以有些 Component 必須額外做null check ,否則在 runtime 時就會有幾秒值是null 但如果有Component 有取用這些值,就會發生runtime error,又或者是這次api request是失敗的。但在response回來前我們根本不會知道這些事情。

在 Dart 中的Sound null safety

在 Dart SDK 2.12 版後就是會預設開啟 Sound null safety,其中最大的前提是

「如果沒有特別說明所有的type都是non-nullable的」

所以我們現在如果真的有個Type有可能是 null 那我們可以用 Type? 來表示這個變數是nullable的,像是下面例子中的 String? apiData

class Foo {
  String? apiData;
  Future<void> fecthData() async {
    await Future.delayed(Duration(seconds: 0), () {
      apiData = 'hello wrold';
    });
  }
}
// ...

final foo = Foo()
foo.apiData.length // 這行會出錯

所以當我們使用 foo.apiData.length 時,就會靜態檢查期間就有error出現:

https://ithelp.ithome.com.tw/upload/images/20210925/201129063hN8JVFB16.png

而如果我們硬要run 就會直接跳出error

https://ithelp.ithome.com.tw/upload/images/20210925/20112906CI9vtEU0GI.png

從這個例子我們就可以看到null safety 對我們開發有多大的幫助,因為我們不用實際run就可以知道哪部分的code可能會有null相關的runtime error。

那我們要怎麼操作nullable type的變數呢?

基本上有兩種方式 ? !

?. 就跟JS的optional chaining一樣:如果存取到null就直接return null而不是直接throw error

print(foo.apiData?.length);
// null

! 則是表示這個變數「現在」一定不是null

我們先宣告一個input type 為 String 的function

String concatString(String input) => input + '---';

// ...
	final foo = Foo();
  await foo.fecthData();
  concatString(foo.apiData);
  concatString(foo.apiData!);
	print(foo.apiData!.length);

https://ithelp.ithome.com.tw/upload/images/20210925/20112906vF3IY5WI2M.png

會發現即使我們是在 fetchData() 後再取用 foo.apiData 這裡的type 依然是 String? ,雖然我們能夠確定他一定有值,但這是靜態檢查不出來的。所以我們可以加上! 讓編譯器知道:

「這個值雖然是nullable type,但它現在一定不是null哦」

所以我們就能將 原本是 String? 的變數放進去只接受 String 的function裡,也能夠正確的調用 .length 了。

還記得在很久很久之前看到的 late

先在一個 class 中先用 final 宣告兩個成員然後其中一個加上 late

late final String a;
  final String b;
  Test(this.b);
  void setInitValue() {
    a = 'a';
  }

https://ithelp.ithome.com.tw/upload/images/20210925/20112906Dv2X9SPGxV.png
如果在constructor沒有放this.b會看到hint 只有跳出 b 需要 initialize

late 只是可以讓 null檢查延遲到運行而不是編譯,所以如果忘記 initialize 在runtime還是會有error跳出來。

  final test = Test('');
  //test.setInitValue();
  print(test.a);

https://ithelp.ithome.com.tw/upload/images/20210925/201129063avbfxTpra.png


今天的程式碼:
https://github.com/zxc469469/dart-playground/tree/Day12/null-safety

Sound null safety 只是很大一部份提升我們在開發時體驗,但這不代表一定不會有bug,畢竟你可能真的沒有去set資料導致你之後! 其實是標爽的,又或者標 late 的變數忘記 initialize,另外還有一個好處就是會提升程式的編譯效率,因為編譯器可以少做一些null check。

而明天將是Dart篇的最後一篇文章,也就是稍微提一下 Functional programming(FP)的概念。之後就要進入Flutter的世界了~


參考資料:

https://dart.cn/null-safety

https://juejin.cn/post/6958965184631144478


上一篇
Day 11 | Dart 非同步 - Stream
下一篇
Day13 | Dart 中的 Functional Programming
系列文
Flutter web 的奇妙冒險30

尚未有邦友留言

立即登入留言