昨天的教學,有教大家 Dart 的變數及流程控制,今天的教學則會說明一些常用的 collections,List 和 Map,並且會提到 Sound null Safety 的概念。在 Flutter 中,有很多地方都會用到 List,也因此掌握 List 的用法對 Flutter 至關重要!
程式碼:https://github.com/ksw2000/ironman-2024
參考官方教學: https://dart.dev.org.tw/language/collections
在 Dart 中,利用中括號代表 list,list 的型態為 List
,也可以指定 List 中的型態,比如 List<String>
, List<int>
等。並且我們可以利用 .add()
的方法追加元素在 List 後面,即 Javascript 中的 push()
var name = ['小櫻', '知世', '小狼'];
name.add('小可');
name.add('雪兔');
print(name);
// [小櫻, 知世, 小狼, 小可, 雪兔]
以下這幾種宣告空 list 的方法也是可行的
List<String> name2 = [];
List name3 = <String>[];
我們也可以利用 ...
來展開 List,使其加入再另一個 list 之後
// 除了用 var 判定型態也可以使用 List 自動判定
List group = ['小狼', '小櫻', '小可'];
List<String> group2 = ['知世', '雪兔', ...group];
print(group2);
// [知世, 雪兔, 小狼, 小櫻, 小可]
List 有一些常用的方法
var name = ['小櫻', '知世', '小狼'];
print(name.first); // 取得第一個元素:
print(name.isEmpty); // list 是否為空
print(name.isNotEmpty); // list 是否不為空
print(name.length); // 取得 list 長度
print(name.last); // 取得最後一個元素
print(name.reversed); // 反轉
// 小櫻
// false
// true
// 5
// 雪兔
// (雪兔, 小可, 小狼, 知世, 小櫻)
reverse() 回傳的是一個 Iterable 類,因此印出時是
()
而不是[]
另外,在走訪一個 list 時,有幾個方法,第一個就是大家最熟悉的方法,從第 0 個開始走訪到最後一個,另外也可以利用語法糖 for-in
的方式取得元素,最後也可以用類似 javascript 的 foreach
+ callback function 來走訪。在 Dart 中,當以 function 為 value 時,中間不用像 Javascript 加上 =>
,直接加入花括號就可以執行了!
for (int i = 0; i < name.length; i++) {
print(name[i]);
}
for (var n in name) {
print(n);
}
name.forEach((element) {
print("$element, ");
});
在 Dart 中,利用花括號代表 map,我們可以直接建立一個空的 map,並利用鍵去賦值。
var first_publish = {};
first_publish['go'] = 2009;
first_publish['rust'] = 2010;
first_publish['kotlin'] = 2011;
first_publish['flutter'] = 2017;
print(first_publish);
// {go: 2009, rust: 2010, kotlin: 2011, flutter: 2017}
此時 first_publish
的型別為 Map<dynamic, dynamic>
,什麼是 dynamic
呢?對於那些無法在編譯前確定的型別,我們都可以用 dynamic
表示,dynamic 可以告訴編譯器不做型別檢查,因此有可能在 runtime 會報錯。類似 typescript 中的 any
;或者你可以認為在 // javascript 的世界裡全部的變數都是 dynamic
。
當然我們也可以在宣告一個非空的 map。此時編譯器可以自動判斷 first_publish2
的型別為 Map<String, int>
var first_publish2 = {
'go': 2019,
'rust': 2010,
'kotlin': 2011,
'flutter': 2017
};
以下這幾種宣告方法也是可行的:
var first_publish3 = <String, int>{};
Map<String, int> first_publish4 = {};
Map 有一些常用的方法
print(first_publish.keys); // 取得鍵的 Iterable 類
print(first_publish.values); // 取得值的 Iterable 類
print(first_publish.length); // 取得 map 長度
print(first_publish.isEmpty); // map 是否為空
print(first_publish.isNotEmpty); // map 是否非空
// (go, rust, kotlin, flutter)
// (2009, 2010, 2011, 2017)
// 4
// false
// true
我們可以利用 .addAll()
將其中一個 map 加入另一個 map 中
var other_first_publish = {
'c': 1972,
'c++': 1983,
'python': 1991,
'php': 1995,
'java': 1995,
'javascript': 1996
};
first_publish2.addAll(other_first_publish);
print(first_publish2);
// {go: 2019, rust: 2010, kotlin: 2011, flutter: 2017, c: 1972, c++: 1983, python: 1991, php: 1995, java: 1995, javascript: 1996}
至於要如何刪除 map 中的鍵值對呢?我們可以利用 .remove()
來實現
first_publish2.remove('php');
print(first_publish2);
// {go: 2019, rust: 2010, kotlin: 2011, flutter: 2017, c: 1972, c++: 1983, python: 1991, java: 1995, javascript: 1996}
我們也可以利用 forEach()
來做走訪,此時的 forEach()
內的 callback function 與 List 不同,Map 的 callback function 可以對應兩個變數 key
和 value
first_publish2.forEach((k, v) {
print("$k -> $v");
});
// go -> 2019
// rust -> 2010
// kotlin -> 2011
// flutter -> 2017
// c -> 1972
// c++ -> 1983
// python -> 1991
// java -> 1995
// javascript -> 1996
當我們在 map 中嘗試取得一個不存在的鍵時,會回傳 null
,null
是一個很特別的存在。由於 null
的存在很容易導致在 runtime 時系統出錯,因此我們先前所教的宣告都不允許 null
出現,也不允許在宣告時不給值。比如以下程式會報錯:
int a;
print(a);
// Error: Non-nullable variable 'a' must be assigned before it can be used.
如果我們想要使變數能使用 null
,我們僅需在型別後加入 ?
即可
int? a;
print(a);
// null
在介紹 List 時,我們利用 ...listA
使 listA
展開,加入另一個 List 中。假如 listA
是一個可為空的型別 List?
時,我們就不能直接使用 ...
將其展開,而必需改用 ...?
。...?listA
代表如果 listA
List? group3;
List group4 = ['知世', '雪兔', ...?group3];
在 Dart 中我們可以利用 ?.
來呼叫方法,當變數為 null
時會自動忽略。
List? group3;
group3?.add(10);
print(group3); // null
如果我們保證這些可為 null
的變數不為空時,也可以用 !.
來呼叫方法。但是一般不建議使用這個方法,因為這有可能導致 runtime 時發生錯誤。
List? group3;
group3!.add(10);
print(group3);
// Unhandled exception:
// Null check operator used on a null value
另外,Dart 也支援 ??
語法糖 a ?? b
,當 a 不為 null
時返回 a,反知返回 b
var a = null;
var b = 10;
print(a ?? b); // 10
?.
, !.
和 ??
的用法跟 Typescript 雷同,也因此只要掌握其中一套語言,就可以融會貫通。