近日,在 Dart 3.8 的 changelog 里正式提交了 Null-Aware Elements 語法,該語法糖可以用于在 List、Set、Map 等集合中處理可能為 null 的元素或鍵值對,簡化顯式檢查 null 的場景:
/之前
var listWithoutNullAwareElements = [if (promotableNullableValue != null) promotableNullableValue,if (nullable.value != null) nullable.value!,if (nullable.value case var value?) value,
];/之后
var listWithNullAwareElements = [?promotableNullableValue,?nullable.value,?nullable.value,
];
自然,在 Flutter 的 UI 聲明里,也可以簡化之前控件的 if 判斷,不得不說確實比起之前的寫法優雅不少:
/之前
Stack(fit: StackFit.expand,children: [const AbsorbPointer(),if (widget.child != null) widget.child!,],
)/之后
Stack(fit: StackFit.expand,children: [const AbsorbPointer(),?widget.child,],
)
同時,官方在分析了大量開源 Dart 代碼后(90019 個文件中的 17,941,439 行代碼),發現這類需要支持的場景更多是 Map
:
-- Surrounding collection (1812 total) --1566 ( 86.424%): Map ===============================================241 ( 13.300%): List ========5 ( 0.276%): Set =
而事實上,從以下例子可以看出來,在簡化 Map
上 Null-Aware Elements 的作用尤為明顯:
/之前
final tag = Tag()..tags = {if (Song.title != null) 'title': Song.title,if (Song.artist != null) 'artist': Song.artist,if (Song.album != null) 'album': Song.album,if (Song.year != null) 'year': Song.year.toString(),if (comments != null)'comment': comms!.asMap().map((key, value) => MapEntry<String, Comment>(value.key, value)),if (Song.numberInAlbum != null) 'track': Song.numberInAlbum.toString(),if (Song.genre != null) 'genre': Song.genre,if (Song.albumArt != null) 'picture': {pic.key: pic},}..type = 'ID3'..version = '2.4';/之后
final tag = Tag()..tags = {'title': ?Song.title,'artist': ?Song.artist,'album': ?Song.album,'year': ?Song.year?.toString(),if (comments != null)'comment': comms!.asMap().map((key, value) => MapEntry<String, Comment>(value.key, value)),'track': ?Song.numberInAlbum?.toString(),'genre': ?Song.genre,if (Song.albumArt != null) 'picture': {pic.key: pic},}..type = 'ID3'..version = '2.4';
通過下面的簡單例子,也可以看出來有了 Null-Aware Elements 之后在代碼簡化效果上很明顯:
當然,配合其他語法也能達到去 null 的效果,比如最簡單的 for 循環,通過 ?i
,就可以簡單到做排除空數據的目的:
當然,你可能會覺得本來 Dart 里就有很多 ? ,比如 ?? 、 ?. 之類,加上語法之后會不會有歧義?這個問題在目前的規則上看起來還行,例如此時的 ?
前通常是 ,
、[
、{
或 :
等符號,這些上下文和現有 ?
用法不同 :
var list = [1, ?foo]; // ? 是空感知元素,不是其他用法
var map = {key: ?value}; // ? 是空感知值,不是可空類型
并且前面介紹過,與現有語法如 if
或 for
元素結合時,?
出現在 if
或 for
頭部后也不會有歧義:
var list = [for (var i in [1, 2]) ?i, // 合法:?i 是空感知元素
];
print(list); // 輸出: [1, 2]
而在 Flutter 里的 UI 編排了就更加直觀了:
當然,這個語法還是有一些規則限制,在這個規則下 expression 只能是一個普通表達式,不能是另一個集合,比如嵌套的 ?
或展開操作 ...
:
element ::=| nullAwareExpressionElement| nullAwareMapElement| // Existing productions...nullAwareExpressionElement ::= '?' expressionnullAwareMapElement ::=| '?' expression ':' '?'? expression // Null-aware key or both.| expression ':' '?' expression // Null-aware value.
例如下方代碼就可以很直觀展示這個錯誤使用,同時也沒有 ????foo
或 ?if (c) nullableThing else otherNullableThing
這樣的場景:
可以看到, Null-aware elements 語法不管是在邏輯代碼還是 UI 代碼都十分有用,雖然 Dart 3.8 還沒正式發布,但是你可以在 Flutter beta channel 提前體驗,那么,你覺這個語法符合你的審美嗎?
參考鏈接
- https://github.com/dart-lang/language/blob/main/accepted/future-releases/0323-null-aware-elements/feature-specification.md