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 25 - [实战练习] 使用 Plugin 建立 Form 注册表单

>>:  【Day 12】逻辑回归(Logistic Regression)(上)

【Day 10】Repository 设计模式(Python)

前言 Repository 设计模式主要是要分离商业逻辑与资料存取的逻辑,希望开发者专注在商业逻辑的...

参赛动机、系列文规划

参赛动机 原本是职缺是应徵网页前端工程师,因为公司目前需要有人帮忙写 App ,就被推坑一起写 Fl...

2.4.5 Design System - Card

忘了在哪看到很喜欢的一段话 每个我们生命中遇见的人 都在我们身上留下了一点影子 以前不懂 但後来发...

Day30 下拉式选单小实作2

接着点选六个按钮(不要点选整个stack view),设定每个按钮的长宽和按钮与按钮的距离。 而後我...

Day04 如何通讯-网路协商

WebRTC 通讯 WebRTC 最常见的应用场景就是一对一的视讯通话,当我们准备和另一端的人进行点...