上個(gè)博客我們大概了解了dart中的基本數(shù)據(jù)類型的特性以及和java、javaScript等語(yǔ)言之間的比較,這篇博客我們主要來(lái)介紹一下dart中的集合操作择葡,動(dòng)態(tài)變量和函數(shù)相關(guān)的內(nèi)容
集合
熟悉java的都知道紧武,在java中集合分為了Map,List和Set三種敏储,每一種職責(zé)和使用方式都不一樣阻星,并且在java中還提供了一個(gè)通用的數(shù)組類型Array用來(lái)更加精細(xì)的處理和集合不一樣的操作,但是我們?cè)谑褂肔ist集合和Array的時(shí)候已添,經(jīng)常會(huì)因?yàn)閿?shù)組和集合之間的互相使用而感覺(jué)到為了詳細(xì)區(qū)分犧牲了體驗(yàn)度妥箕,而js中就很簡(jiǎn)單,因?yàn)閖s中并不限制你是什么類型酝碳,可以直接初始化一個(gè)[]作為數(shù)組或者集合使用矾踱,并且可以初始化添加多個(gè)參數(shù)恨狈,還可以在初始化以后繼續(xù)push的方式添加元素進(jìn)入集合中疏哗,可以說(shuō)比起java這方面操作上的確好了不少,但是缺陷也很明顯禾怠,js的集合不能強(qiáng)制指定類型返奉,可以任意更改,并且js中集合單一吗氏,并無(wú)map芽偏,set等具體的細(xì)節(jié)區(qū)分。dart作為一個(gè)新生代的語(yǔ)言弦讽,既然能集百家之長(zhǎng)污尉,肯定是要具備java和js這些比較優(yōu)秀的特性,并且dart綜合了兩大語(yǔ)言的特點(diǎn)往产,摒棄了一些不好的體驗(yàn)
List
dart的list既有數(shù)組特性也有java中的list特性被碗,接下來(lái)我們看一下使用:
void main() {
List list = new List();
list.add("aa");
list.add(1);
list.add(1.2);
print(list);
}
看到這里,熟悉java的一定會(huì)認(rèn)為報(bào)錯(cuò)仿村,但是輸出的結(jié)果卻是如下所示锐朴,是不是很驚訝?其實(shí)要明白蜘腌,dart中默認(rèn)為list的類型為最大的基類Object恶复,所以如果我們不指定泛型的話录别,會(huì)當(dāng)成List<Ob
ject>方式,所以這里能正確輸出
[aa, 1, 1.2]
接著我們指定一下泛型為int類型以后酱酬,再來(lái)看看情況
很明顯,編譯時(shí)期就報(bào)錯(cuò)了云矫,因?yàn)閺?qiáng)制指定了泛型的原因岳悟,而在開(kāi)發(fā)中,我們往往有時(shí)候希望初始化的時(shí)候就設(shè)置幾個(gè)參數(shù)進(jìn)去,然后再去做add等操作贵少,傳統(tǒng)java類型的list.add操作可能要寫(xiě)很多次呵俏,這樣的話初始化會(huì)很不方便,能不能像數(shù)組那樣直接初始化插入值滔灶,然后后面有需要的話再去add呢普碎?如果是在java中,這樣的操作是不能實(shí)現(xiàn)的录平,因?yàn)閿?shù)組的長(zhǎng)度是固定的麻车,而list又沒(méi)有類型數(shù)組那樣的初始化賦值方式,但是在js中數(shù)組初始化賦值是個(gè)最常見(jiàn)不過(guò)的事情斗这,并且還支持后面push操作动猬,在dart中,也是支持初始化賦值后期add元素的操作的表箭,如下:
void main() {
List<int> list = [1,2,3];
list.add(1);
print(list);
}
并且在dart中提供了一系列語(yǔ)法糖api赁咙,不僅支持add單個(gè)元素,支持addAll添加一個(gè)集合進(jìn)行合并免钻,還支持多種remove操作彼水,并且還有replaceRange 這樣的語(yǔ)法糖可以進(jìn)行元素的替換操作,集合的api在java和js中都有所不足极舔,dart在此基礎(chǔ)上進(jìn)行了改進(jìn)操作
Map
map是個(gè)key-value方式存儲(chǔ)數(shù)據(jù)的集合凤覆,這點(diǎn)在java語(yǔ)言中體現(xiàn)的很多,dart中完美的繼承了過(guò)來(lái),并且提供了類似java的api拆魏,但是在dart中不存在接口的概念盯桦,所以可以直接new Map出來(lái):
Map<String,Object> map = new Map<String,Object>();
map['abc'] = 'bbb';
print(map);
可以看出來(lái)dart中的map使用和js中一樣,直接指定key進(jìn)行賦值就可以了渤刃,并且支持key-value的泛型拥峦,還支持了一些常用的語(yǔ)法糖操作:
map.containsKey("abc"); //是否存在當(dāng)前key true
map.containsValue("bbb");//是否存在當(dāng)前value true
map.isEmpty;//false
map.isNotEmpty;//true
map.keys;//獲取所有的key [abc]
map.values;//獲取所有的value [bbb]
可以看出來(lái),dart中的map比起java的map的api支持還是很不錯(cuò)的溪掀,并且針對(duì)java中map無(wú)法訪問(wèn)value的問(wèn)題做了改進(jìn)事镣,直接提供了api可以操作value
Set
dart中保留了Set集合這個(gè)設(shè)計(jì)(和java保持一致,這個(gè)優(yōu)良特性被保留了下來(lái))揪胃,但是在官網(wǎng)的文檔也好璃哟,包括flutter的文檔都沒(méi)有提到Set這個(gè)類型,但是測(cè)試下來(lái)的確是按照java的Set設(shè)計(jì)的Set類型喊递,接下來(lái)看看Set的基本使用:
void main(){
Set set = new Set();
Set newSet = new Set();
newSet.add("bac");
set.add("daba");
set.remove("daba");
Set unionSet = set.union(newSet);//可以和其他的set拼接随闪,返回合并的集合
set.add(1);
set.add([1,2,3]);
print(set);//{1, [1, 2, 3]}
print(unionSet);//{bac}
}
從上面可以看出來(lái)set的基本使用和java中幾乎一樣,但是常用的api中多了一個(gè)union骚勘,官方的注釋如下:
/**
* Returns a new set which contains all the elements of this set and [other].
*
* That is, the returned set contains all the elements of this [Set] and
* all the elements of [other].
*/
Set<E> union(Set<E> other);
可以看出來(lái)大概是說(shuō)铐伴,將兩個(gè)set合并成一個(gè)set撮奏,返回出去,也就是說(shuō)這個(gè)方法是不改變?cè)瓉?lái)的兩個(gè)set的結(jié)構(gòu)当宴,只是將元素取出來(lái)畜吊,返回新的set,那么就有一個(gè)問(wèn)題户矢,這個(gè)新的set是否會(huì)根據(jù)兩個(gè)合并的set的元素添加或者刪除跟著改變?cè)啬?在js中的合并操作可以根據(jù)新增或者修改或者刪除動(dòng)態(tài)的改變)玲献?
void main(){
Set set = new Set();
Set newSet = new Set();
newSet.add("bac");
set.add("daba");
set.remove("daba");
set.add(1);
set.add(1);
set.add([1,2,3]);
Set unionSet = set.union(newSet);
print(set);//{1, [1, 2, 3]}
print(unionSet);//{1, [1, 2, 3], bac}
}
可以看出來(lái)我們只是多了一個(gè)重復(fù)添加1的操作,并且將最后的合并方法放在了兩個(gè)set的添加元素之后梯浪,很明顯最終輸出的結(jié)果和上面的結(jié)果不一樣捌年,所以我們可以得出結(jié)論,set的union操作是把兩個(gè)set的元素單獨(dú)遍歷取出來(lái)存入的新的set而不是動(dòng)態(tài)合并的操作挂洛,所以可以看出來(lái)dart的set特性和java的一樣
集合遍歷
在java中l(wèi)ist遍歷的方法很多礼预,for i循環(huán)或者for增強(qiáng)循環(huán),或者迭代器遍歷都可以虏劲,Map也支持keys循環(huán)取值或者for增強(qiáng)以及迭代器,set也有for增強(qiáng)和迭代器的遍歷方案托酸,dart中自然保留了這些特性,但是在dart中伙单,for增強(qiáng)循環(huán)取消获高,取而代之的是js中的forEach循環(huán)哈肖,比起for增強(qiáng)吻育,可以傳遞當(dāng)前的每一個(gè)item,并且可以傳遞當(dāng)前的index索引淤井,比起java的for增強(qiáng)不能獲取當(dāng)前的索引來(lái)說(shuō)可玩性好了很多
void main(){
Map<String,Object> map = new Map<String,Object>();
map['abc'] = 'abc';
map['bbb'] = 'abc';
map.forEach((key,value){
print(key+",value:"+value);//abc,value:1 bbb,value:3
});
List list = ['1','a','pdc'];
for(int i = 0;i<list.length;i ++){
print('value:'+list[i]); //value:1 value:a value:pdc
}
Set set = {'abc',1212,'bbb'};
//set和list之間可以直接使用toList或者toSet方法進(jìn)行轉(zhuǎn)換布疼,但是map不可以直接進(jìn)行轉(zhuǎn)換!map需要轉(zhuǎn)換成list或者set可以選擇獲取keys/values進(jìn)行toList/toSet轉(zhuǎn)換操作
set.toList().forEach((value){
print(value); //abc 1212 bbb
});
}
動(dòng)態(tài)變量類型
在dart中加入了動(dòng)態(tài)變量類型的特性,不僅包含了js和java8以后加入的var币狠,并且支持Object基類游两,還加入了kotlin的dynamic關(guān)鍵字,接下來(lái)我們看看這三種動(dòng)態(tài)變量
var
var的特性我們?cè)趈s中都很熟悉漩绵,可以隨意給var類型的變量賦值贱案,但是在dart中,var只是可以讓你后期再申明具體的類型止吐,并不是可以任意改動(dòng)類型(面向?qū)ο笳Z(yǔ)言通病)宝踪,如果你在申明了一次變量以后,再去設(shè)置一個(gè)新的類型碍扔,那么編譯器就會(huì)直接拋出異常
var t;
t="hi world";
t=1000;// 下面代碼在dart中會(huì)報(bào)錯(cuò)瘩燥,因?yàn)樽兞縯的類型已經(jīng)確定為String,
dynamic
在dart中存在一個(gè)情況不同,我們前期不確定這個(gè)類型厉膀,但是后期又想修改掉怎么辦溶耘?var不能修改類型,有木有這種完全動(dòng)態(tài)的類型呢服鹅?有凳兵,dynamic關(guān)鍵字就是完全動(dòng)態(tài)的類型,和var的區(qū)別在于企软,var是在編譯期就確定了類型留荔,也就是說(shuō)當(dāng)?shù)谝淮紊昝饕院缶痛_定了類型,而dynamic是屬于運(yùn)行時(shí)動(dòng)態(tài)類型澜倦,也就是說(shuō)你可以任意修改類型或者申明聚蝶,只要最終運(yùn)行的時(shí)候能確定類型,使用如下:
dynamic t;//很坑的是這個(gè)類型目前的dart插件居然沒(méi)有代碼提示必須全部寫(xiě)完才出現(xiàn)藻治,不能判斷正確性
t = "hi world";
t = 1000;//可以進(jìn)行修改申明類型
print(t);//輸出為:1000
Object
dart這個(gè)語(yǔ)言和java一樣碘勉,一切都是對(duì)象,并且一切對(duì)象的基類都是Object桩卵,所以在dart中同樣支持使用Object來(lái)申明任意類型验靡,并且Object無(wú)限制可以接受任何類型,那么Object和dynamic的區(qū)別在哪呢雏节??jī)烧叨伎梢詣?dòng)態(tài)接受類型并且都是運(yùn)行時(shí)確定類型的啊胜嗓,別急,我們看一個(gè)簡(jiǎn)單的案例:
dynamic a = "a";
Object b = "b";
void main() {
print(a.length);//這里可以調(diào)用length屬性钩乍,完美輸出
print(b.length);//這里是object類型就不能調(diào)用length方法辞州,編譯就報(bào)錯(cuò)
}
從上面的案例就能看出來(lái)區(qū)別,dynamic屬于動(dòng)態(tài)改變的類型寥粹,也就是說(shuō)編譯器會(huì)根據(jù)你申明的類型自動(dòng)推斷為該類型变过,所以該類型的所有的屬性都可以使用,但是使用Object的類編譯器無(wú)法推斷具體類型涝涤,默認(rèn)為還是Object類型媚狰,所以只能調(diào)用Object的屬性和方法,而Object中不存在length阔拳,所以就會(huì)報(bào)錯(cuò)崭孤,所以總結(jié)下來(lái),Object是需要手動(dòng)轉(zhuǎn)換具體類型來(lái)進(jìn)行操作糊肠,而dynamic屬于自動(dòng)推斷類型辨宠,調(diào)用該類型的屬性,靈活度來(lái)說(shuō)dynamic更高罪针,但是按照代碼的嚴(yán)謹(jǐn)性和可讀性來(lái)說(shuō)Object更高更不容易出錯(cuò)彭羹,所以一般來(lái)說(shuō),在大部分代碼不是很嚴(yán)格的環(huán)境下泪酱,我們會(huì)選擇dynamic來(lái)替代Object進(jìn)行開(kāi)發(fā)派殷,提高靈活度
常量
const
在js我們申明變量有var还最,es6開(kāi)始又加入了let和const兩種類型,其中const代表的就是固定的值毡惜,一旦申明一次就不可以再去修改的變量拓轻,這點(diǎn)和java中的final很相似,dart中同時(shí)包含了js的const和final兩個(gè)常量類型经伙,在dart中const是編譯常量扶叉,也就是說(shuō)當(dāng)const修飾了這個(gè)變量以后,如果再去修改帕膜,編譯器直接就報(bào)錯(cuò)(這里應(yīng)該是完全沿襲js中的const)
const str1 = "hi world";
str1 = "aaa";//編譯器直接報(bào)錯(cuò)
var list = const [1, 2, 3]枣氧;//const還可以修飾變量對(duì)應(yīng)的值,強(qiáng)制讓這個(gè)值也變成固定的
final
dart中有了編譯期的常量const以后垮刹,也加入了運(yùn)行時(shí)的常量final關(guān)鍵字达吞,final和const都是常量,區(qū)別在于作用范圍荒典,final只能修飾變量酪劫,并且確定變量的動(dòng)態(tài)賦值類型以后就固定了,不能再去修改寺董,而const不僅僅可以修飾變量覆糟,還可以修飾值,比如var list = const [1, 2, 3]就是通過(guò)const修飾了一個(gè)數(shù)組遮咖,這個(gè)數(shù)組的值是固定的滩字,不能變,與修飾在變量上作用范圍是不一樣的
final str1 = "hi world";
str1 = "bbb";//這里也會(huì)報(bào)錯(cuò)
并且使用const修飾的常量可以作為構(gòu)造盯滚,如果構(gòu)造是完全一樣的踢械,這時(shí)候使用的是同一個(gè)實(shí)例酗电,用于提高編譯和運(yùn)行的效率
getConst() => const [1, 2];
main() {
var a = getConst();
var b = getConst();
identical(a, b); // =>true
}
static
dart中的static作用很弱魄藕,只能用來(lái)修飾成員變量,在類中使用撵术,不可以在方法中給某個(gè)局部變量修飾背率,并且這個(gè)修飾的對(duì)象在運(yùn)行期才會(huì)被初始化
void main(){
print(Test.a);
}
class Test{
static String a = "aaa";//只能寫(xiě)在類成員變量上,在方法或者main等修飾都不可以
}
函數(shù)
dart中有一種特殊的類型--函數(shù)類型Function嫩与,該函數(shù)可以在開(kāi)發(fā)的時(shí)候傳遞使用寝姿,也可以進(jìn)行賦值傳遞,這點(diǎn)和js完全相同
void main(){
Function fun = getBool;//申明一個(gè)函數(shù)划滋,后面就可以直接傳遞該函數(shù)了
}
bool getBool(){
return false;
}
需要注意的是饵筑,dart中函數(shù)不設(shè)置返回值,也可以处坪,因?yàn)榇嬖陬愋蚫ynamic類型根资,但是需要注意的是架专,這個(gè)時(shí)候返回值不會(huì)是動(dòng)態(tài)推斷類型,而是固定的dynamic 類型玄帕,不會(huì)跟著返回的內(nèi)容具體類型自動(dòng)變動(dòng)
//這里我們返回的是bool類型部脚,但是不設(shè)置類型返回的不是bool,而是dynamic
isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
if(isNoble(1)){//這里編譯就會(huì)報(bào)錯(cuò)裤纹,因?yàn)椴皇莃ool類型
}
上面說(shuō)到函數(shù)還可以進(jìn)行傳遞委刘,可以申明成變量,也可以直接當(dāng)成參數(shù)進(jìn)行傳遞:
//函數(shù)作為變量直接使用
var say= (str){
print(str);//輸出-->hi world
};
say("hi world");
//函數(shù)作為參數(shù)進(jìn)行傳遞
void execute(var callback){
callback();//一樣可以把參數(shù)當(dāng)成函數(shù)調(diào)用鹰椒,前提是這參數(shù)傳來(lái)的時(shí)候是個(gè)函數(shù)锡移,否則報(bào)錯(cuò)
}
execute(()=>print("xxx"))
可選位置參數(shù)與可選命名參數(shù)
可選位置參數(shù)
我們?cè)陂_(kāi)發(fā)java的過(guò)程中,方法中的參數(shù)可能有多個(gè)可能有一個(gè)漆际,所以我們經(jīng)常定義一個(gè)方法罩抗,有多個(gè)參數(shù),不需要這個(gè)參數(shù)的時(shí)候傳遞null占位灿椅,或者我們需要重載多個(gè)一樣的方法套蒂,只是參數(shù)不一樣,進(jìn)行不一樣的業(yè)務(wù)開(kāi)發(fā)茫蛹,可以說(shuō)雖然很嚴(yán)格操刀,但是靈活度等都下降了不少,dart中同樣支持傳遞幾個(gè)固定的參數(shù)婴洼,但是dart在這個(gè)基礎(chǔ)上改進(jìn)了骨坑,分為必傳參數(shù)和可不傳參數(shù),如果存在多個(gè)參數(shù)柬采,我們固定了幾個(gè)必傳的參數(shù)欢唾,其他的參數(shù)不確定是否需要傳遞,我們可以使用[]包裹不確定的參數(shù)粉捻,這樣我們使用的時(shí)候可以傳該參數(shù)也可以不傳礁遣,但是這里需要注意的是,[]包裹的參數(shù)類型和位置是固定的肩刃,這點(diǎn)和java的多參數(shù)一樣祟霍,傳遞的時(shí)候必須按照對(duì)應(yīng)的位置一一對(duì)應(yīng)傳遞,唯一的區(qū)別在于盈包,這個(gè)參數(shù)不傳遞的時(shí)候可以不寫(xiě)沸呐,而不是傳遞null(因?yàn)槟J(rèn)值就是null)
void main(){
print(say("form","msg"));//-->form says msg
print(say("form","msg","bbb"));//-->form says msg with a bbb
}
//這里的device固定了類型為String,固定了下標(biāo)為2呢燥,傳遞的時(shí)候必須按照這個(gè)順序傳遞崭添,可以不傳
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
但是,熟悉js的人都知道叛氨,在js中我們可以隨便指定參數(shù)呼渣,也可以在函數(shù)無(wú)參數(shù)的情況下根暑,調(diào)用方法的時(shí)候傳遞參數(shù),只要在函數(shù)內(nèi)部調(diào)用arguments獲取當(dāng)前傳遞的參數(shù)數(shù)量和類型徙邻,做自己的業(yè)務(wù)處理就可以了排嫌,(因?yàn)閖s函數(shù)和變量名不可以重復(fù),所以不存在重載)但是這么做的壞處很明顯缰犁,我們不能很清晰明了的看到可能有哪些參數(shù)淳地,對(duì)于開(kāi)發(fā)和可讀性而言不是特別友好,dart為了改進(jìn)這種情況帅容,特意出了plus版本--可選命名參數(shù)
可選命名參數(shù)
可選命名參數(shù)和上述的可選位置參數(shù)大體使用相同颇象,不是必傳的參數(shù)需要用括號(hào)包裹,但是區(qū)別是并徘,這里的參數(shù)使用{}包裹(當(dāng)然也可以把必傳參數(shù)也包裹進(jìn)來(lái)遣钳,只需要在參數(shù)前加入一個(gè)必傳的注解@required,但是這個(gè)注解是flutter的庫(kù)才有麦乞,dart開(kāi)發(fā)的時(shí)候不可以使用蕴茴,只能選擇固定參數(shù)獨(dú)立出來(lái),包裹動(dòng)態(tài)參數(shù))
flutter中我們可以這么寫(xiě):
import 'package:flutter/material.dart';
void main(){
print(say(from:"form",msg:"msg"));//-->form says msg
print(say(from:"form",msg:"msg",device:"bbb"));//-->form says msg with a bbb
}
String say({@required String from,@required String msg,String device}) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
dart中可以這么寫(xiě):
void main(){
print(say("form","msg"));//-->form says msg
print(say("form","msg",device:"bbb"));//-->form says msg with a bbb
}
String say(String from,String msg,{String device}) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}