啰啰嗦嗦的前言
軟件發(fā)展的歷史印證了應(yīng)用層開發(fā)必然是OOP(Object Oriented Programming)(面向?qū)ο缶幊陶Z言)語言的天下,安卓應(yīng)用開發(fā)之前一直是使用jvm系語言(最開始是java,后來又有kotlin)翘地,但是java由于需要兼顧全平臺(tái),很多地方并不是為移動(dòng)端量身定做迫悠,這就導(dǎo)致了某些方面無法做到最優(yōu)携添,再加上java易主后oracle跟谷歌在版權(quán)問題上的各種不愉快,可能就導(dǎo)致了谷歌想降低安卓平臺(tái)對(duì)java的依賴叛本,后來就有了flutter沪蓬,flutter使用dart語言。
扯遠(yuǎn)了炮赦,關(guān)鍵字只有OOP,相比java样勃,dart是更徹底的OOP吠勘,一切都是對(duì)象性芬。那么就從類和對(duì)象學(xué)起。
類的定義
寫法跟JVM系語言都差不多剧防,隨便寫一個(gè):
abstract class Animal{
//普通構(gòu)造器
Animal(String name, int age):
assert(name != null),
assert(age > -1),
this.name = name, this.age = age{
print("Animal's constructor is invoked");
}
//命名構(gòu)造器的轉(zhuǎn)發(fā)寫法植锉,調(diào)用自己的其他構(gòu)造器
//注意一旦這么寫了,后面就不能接函數(shù)體了
Animal.fromMap(Map map):
this(map["name"] ?? "unknownAnimal", map["age"] ?? 0);
//命名構(gòu)造器的非轉(zhuǎn)發(fā)寫法峭拘,可以接函數(shù)體
Animal.fromDynamic(dynamic object){
this.name = object.name;
this.age = object.age;
}
String name;
int age;
//來個(gè)抽象方法俊庇,這里不寫abstract,沒有方法體的方法就是抽象方法鸡挠。
//動(dòng)物當(dāng)然得會(huì)動(dòng)才行
void move();
}
上面的普通構(gòu)造器其實(shí)可以簡化辉饱,變成這樣:
Animal(this.name, this.age):
assert(name != null),
assert(age > -1){
print("Animal's constructor is invoked");
}
這種寫法的含義是,構(gòu)造器接受兩個(gè)跟自己的域成員類型一致的變量拣展,分別賦值給它們彭沼,因?yàn)槲覀兌x的name是String,age是int备埃,所以這里就需要第一個(gè)參數(shù)是String而第二個(gè)參數(shù)是int姓惑,assert語句會(huì)在賦值之前被執(zhí)行。
注意:普通構(gòu)造器的名字跟類名相同按脚,而且每個(gè)類只能有一個(gè)普通構(gòu)造器
于毙。
下面這種寫法用來讓所有對(duì)構(gòu)造器的調(diào)用都產(chǎn)生相同的對(duì)象:
class Planet{
static String description = "這是一個(gè)行星,一旦命名就不會(huì)改名";
static int orbitInvokedCount;
final String name;
const Planet(this.name);
void showOrbit(){
print("本來應(yīng)該讓你看到我運(yùn)行的軌道辅搬,但想想還是算了");
print("算上你唯沮,一共${++orbitInvokedCount}個(gè)傻帽被我氣的發(fā)飆");
}
}
如果這么寫,類里面所有非靜態(tài)成員都必須是final伞辛。這種類也是可以被繼承的烂翰。
繼承(extends)
隨便寫個(gè)子類:
class Rabbit extends Animal{
Rabbit(String name, int age) : super(name, age);
@override
void move() {
print("兔子蹦蹦跳跳的往前走");
}
}
一個(gè)類如果不顯示指定構(gòu)造器,那么它就有一個(gè)無參的普通構(gòu)造器蚤氏,如果指定了有參構(gòu)造器甘耿,子類就必須在它和其他命名構(gòu)造器里選擇一個(gè)實(shí)現(xiàn)(如果有命名構(gòu)造器),以上例子我選擇了實(shí)現(xiàn)普通構(gòu)造器竿滨,當(dāng)然也可以實(shí)現(xiàn)命名構(gòu)造器:
class Rabbit extends Animal{
Rabbit.fromMapToo(Map map) : super.fromMap(map);
@override
void move() {
print("兔子蹦蹦跳跳的往前走");
}
}
可以看到佳恬,命名構(gòu)造器不一定要跟父類同名,簡單來說于游,有辦法能把這個(gè)類實(shí)例化就行毁葱。不管是普通構(gòu)造器還是什么途徑都行。
實(shí)現(xiàn)(implements)
注意:在dart里贰剥,一個(gè)類在定義的時(shí)候就同時(shí)產(chǎn)生了兩個(gè)東西倾剿,一個(gè)是你寫的完整定義以及行為
,另一個(gè)是由他們抽象出來的接口
,也就是說前痘,當(dāng)我定義了兔子類的時(shí)候凛捏,這個(gè)東西就同時(shí)代表兩個(gè)概念:兔子
這個(gè)類和像兔子的
這個(gè)接口,它們的名字都是Rabbit芹缔。跟java不同坯癣,這里類的屬性也是接口的一部分,估計(jì)“得益于”getter最欠、setter語法糖示罗。。芝硬。(無奈臉)
class RabbitDoll implements Rabbit{
@override
int age = 0;
@override
String name = "兔子娃娃";
//這個(gè)類的構(gòu)造函數(shù)不受Rabbit的約束
RabbitDoll(this.age, this.name);
RabbitDoll.fromWhat();
@override
void move() {
print("兔子布偶不會(huì)走");
}
}
混合(mixin)
混合的概念是把類的方法放入另一個(gè)類蚜点,這在jvm語言上應(yīng)該是以組合的形式實(shí)現(xiàn)的,dart具體實(shí)現(xiàn)方式不明吵取,上代碼說明:
class Flyable{
String trait = "會(huì)飛";
void move(){
print("老子會(huì)飛");
}
void showTrait(){
print("這個(gè)東西的特征是$trait");
}
}
class Sparrow with Flyable implements Animal{
@override
int age;
@override
String name;
}
class Dove extends Animal with Flyable{
Dove(String name, int age) : super(name, age);
}
之前定義的animal類有一個(gè)抽象方法move禽额,這個(gè)方法已經(jīng)被Flyable實(shí)現(xiàn),下面的Sparrow和Dove類分別以implements和extends的方式獲得Animal的接口特征皮官,具體實(shí)現(xiàn)都委托給被混入的Flyable了脯倒。
Dart的類型系統(tǒng)里是單繼承
、多實(shí)現(xiàn)
捺氢、多混合
藻丢,凡是帶“多”的都是逗號(hào)分隔,這就勢(shì)必考慮到一個(gè)問題那就是沖突問題摄乒,implements里不存在具體實(shí)現(xiàn)所以沒有沖突問題悠反,而多mixin的情況由于mixin類都有具體實(shí)現(xiàn),如果一個(gè)特征相同的方法在多個(gè)mixin里都有實(shí)現(xiàn)那會(huì)如何馍佑?實(shí)驗(yàn)證明斋否,寫在后面的mixin會(huì)覆蓋前面的實(shí)現(xiàn)。你調(diào)用這個(gè)方法時(shí)以最后的mixin為準(zhǔn)拭荤,當(dāng)然茵臭,前提是你方法體里不再次覆蓋這個(gè)方法。而mixin的優(yōu)先級(jí)又是高于父類實(shí)現(xiàn)的舅世。
一句話總結(jié):方法的優(yōu)先級(jí):自己的 > mixin列表[n]的 > mixin列表[0]的 > 父類的
旦委。一旦沖突,優(yōu)先級(jí)低的就被覆蓋掉雏亚。
mixin其實(shí)還有一個(gè)寫法缨硝,是用mixin關(guān)鍵字定義:
mixin AnimalLike implements Animal{
}
這種寫法跟class的區(qū)別是:無法被實(shí)例化,不能extends其他類罢低,只能implements接口查辩,可以包含抽象成員。它可以使用on
關(guān)鍵字限定只有特定類(及其子類)能混合它:
mixin AnimalLike on Dove implements Animal{
}
如果這么寫了,只有Dove和Dove的子類可以with它宜岛,當(dāng)然匀钧,on關(guān)鍵字后面是可以跟多個(gè)類的,用逗號(hào)分隔谬返。
注意:這里說的是子類而不是接口,如果你用on限定了一個(gè)類日杈,類X implements了它遣铝,那X是沒法with這個(gè)mixin的。
一個(gè)類繼承A混合了BCD莉擒,它的類型到底是什么酿炸?
這個(gè)問題其實(shí)沒什么意義,之前說過涨冀,dart在聲明一個(gè)類的時(shí)候就同時(shí)產(chǎn)生了同名的接口填硕,而無論是is操作符(對(duì)應(yīng)java的instanceof)還是泛型,里面寫的其實(shí)都是上述的同名接口鹿鳖,我們?cè)谑褂眠@些對(duì)象時(shí)關(guān)心的也只是它的方法特征(輸入輸出)而不是具體實(shí)現(xiàn)扁眯。
但如果硬要說的話,按java里那種父類的概念翅帜,子類的構(gòu)造器要追隨父類這倒是沒錯(cuò)的姻檀。
var sparrow = Sparrow();
var dove = Dove("鴿子", 1);
sparrow.move();
sparrow.showTrait();
var animals = List<Animal>();
var flyables = List<Flyable>();
animals.add(sparrow);
flyables.add(sparrow);
animals.add(dove);
flyables.add(dove);
dove和sparrow既是Animal也是Flyable,不同的場(chǎng)景關(guān)注點(diǎn)不同涝滴,我們想聯(lián)系一個(gè)朋友绣版,那么微信和電話都能實(shí)現(xiàn)這個(gè)功能,這里我們就只關(guān)注通訊這個(gè)功能歼疮,至于微信還能做什么其他的杂抽,在這個(gè)應(yīng)用場(chǎng)景就被忽略了。
訪問限定修飾符
在dart里以_開頭的是私有成員韩脏,跟es6一樣缩麸,沒什么好說的。