假設(shè)我們需要實(shí)現(xiàn)一個(gè)動(dòng)物世界的功能。Animal作為基類(lèi)派生出哺乳類(lèi)、鳥(niǎo)類(lèi)、魚(yú)類(lèi)三種類(lèi)型,各個(gè)類(lèi)型又能派生出具體的動(dòng)物。每種動(dòng)物都具有步行、游泳、飛行三種能力中的某幾種能力:

由于Java和kotlin都不允許多繼承,我們可以將walk、swim、fly定義成interface,讓各個(gè)具體的動(dòng)物類(lèi)去實(shí)現(xiàn)這幾個(gè)接口。在java7里面需要在不同動(dòng)物類(lèi)中寫(xiě)同樣的實(shí)現(xiàn)代碼,但如果用java8或者kotlin,可以在interface中編寫(xiě)默認(rèn)實(shí)現(xiàn)去避免重復(fù)代碼。而在dart中我們要怎么實(shí)現(xiàn)呢?
dart extends & implements
首先dart里面是沒(méi)有interface的, 但是我們可以把class當(dāng)做接口被實(shí)現(xiàn)。使用implements把某個(gè)class當(dāng)做接口來(lái)實(shí)現(xiàn)要求我們重寫(xiě)這個(gè)class的所有方法, 而使用extends繼承某個(gè)class則可以繼承父類(lèi)實(shí)現(xiàn)了的方法:
class Base {
void foo1() {
}
void foo2() {
}
}
class Child1 implements Base {
@override
void foo1() {
// TODO: implement foo1
}
@override
void foo2() {
// TODO: implement foo2
}
}
class Child2 extends Base {
}
implements會(huì)將class的實(shí)現(xiàn)抹掉就不存在默認(rèn)實(shí)現(xiàn)一說(shuō),而dart也是不允許多繼承的。那么我們只能將walk、swim、fly三個(gè)接口在不同動(dòng)物類(lèi)中重復(fù)實(shí)現(xiàn)一遍嗎?
其實(shí)dart里有個(gè)叫做mixin的概念可以解決上面的問(wèn)題
mixin
mixin實(shí)際上也是面向?qū)ο缶幊讨械母拍?在維基百科上對(duì)它的解釋如下:
Mixin是面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言中的類(lèi),提供了方法的實(shí)現(xiàn)。其他類(lèi)可以訪(fǎng)問(wèn)mixin類(lèi)的方法而不必成為其子類(lèi)。[1]Mixin有時(shí)被稱(chēng)作"included"而不是"inherited"。mixin為使用它的class提供額外的功能,但自身卻不單獨(dú)使用(不能單獨(dú)生成實(shí)例對(duì)象,屬于抽象類(lèi))。因?yàn)橛幸陨舷拗疲琈ixin類(lèi)通常作為功能模塊使用,在需要該功能時(shí)“混入”,而且不會(huì)使類(lèi)的關(guān)系變得復(fù)雜。用戶(hù)與Mixin不是“is-a”的關(guān)系,而是“-able”關(guān)系
dart語(yǔ)言里面我們可以使用with關(guān)鍵字實(shí)現(xiàn)mixin,將一個(gè)或者多個(gè)class混入另一個(gè)類(lèi):
class Base1 {
void foo1() {
print("foo1");
}
}
class Base2 {
void foo2() {
print("foo2");
}
}
class Child2 with Base1, Base2 {
}
沒(méi)錯(cuò),通過(guò)with多個(gè)類(lèi),可以實(shí)現(xiàn)類(lèi)似多繼承的效果。
既然允許with多個(gè)類(lèi),那么如果這些類(lèi)中有個(gè)相同方法,那會(huì)出現(xiàn)什么事情。實(shí)際上kotlin、java8使用接口的默認(rèn)實(shí)現(xiàn)也會(huì)出現(xiàn)一樣的問(wèn)題,他們的處理方法是當(dāng)出現(xiàn)相同方法的時(shí)候?qū)崿F(xiàn)類(lèi)需要手動(dòng)指定使用哪個(gè)接口的默認(rèn)實(shí)現(xiàn),要不然編譯會(huì)報(bào)錯(cuò):
// java8
interface IBase1 {
default void foo() {
System.out.println("1");
}
}
interface IBase2 {
default void foo() {
System.out.println("2");
}
}
class Child implements IBase1, IBase2 {
@Override
public void foo() {
IBase2.super.foo();
}
}
//kotlin
interface IBase1 {
fun foo() {
println("1")
}
}
interface IBase2 {
fun foo() {
println("2")
}
}
class Child : IBase1, IBase2 {
override fun foo() {
super<IBase2>.foo()
}
}
線(xiàn)性化
而在dart with里面越后面的類(lèi)優(yōu)先級(jí)越高:
class Base1 {
void foo() {
print("1");
}
}
class Base2 {
void foo() {
print("2");
}
}
class Base3 {
void foo() {
print("3");
}
}
class Child extends Base1 with Base2, Base3 {}
這個(gè)時(shí)候調(diào)用Child.foo方法實(shí)際會(huì)優(yōu)先調(diào)用Base3.foo。原因是dart實(shí)際是通過(guò)創(chuàng)建中間類(lèi)繼承實(shí)現(xiàn)的mixin,上面的代碼相當(dāng)于:

通過(guò)從左到右的順序生成中間父類(lèi)去繼承將extends、with線(xiàn)性化成一個(gè)單繼承鏈。所以Base2、Base3實(shí)際上不是Child的父類(lèi)
mixin關(guān)鍵字
在上面的例子中我們使用普通的class去with,但dart實(shí)際上提供了一個(gè)mixin關(guān)鍵字,它定義了不能實(shí)例化,也不能extends只能with的類(lèi):
mixin Base {
}
// 編譯失敗: mixin類(lèi)不能extends
// class Child extends Base {
//
// }
// 編譯成功: mixin類(lèi)可以with
class Child with Base {
}
void main() {
// 編譯失敗: mixin類(lèi)不能實(shí)例化
// Base()
}
這樣的類(lèi)實(shí)際上和java、kotlin里面的interface已經(jīng)很像了。
另外我們可以通過(guò)mixin ... on 限定某個(gè)類(lèi)只能由某些類(lèi)去with:
class Base1 {
void foo() {
print("1");
}
}
class Base2 {
void foo() {
print("2");
}
}
mixin Base3 on Base1 {
void foo() {
super.foo();
print("3");
}
}
class Child extends Base1 with Base2, Base3 {}
上面的demo中Base3只能由Base1去with,那就以為著這個(gè)with Base3的類(lèi)一定是繼承或者with了 Base1,所以可以調(diào)用這個(gè)類(lèi)的super.foo方法。要注意的是,這個(gè)super.foo并不指定一定調(diào)用的是Base1.foo。例如上面的代碼調(diào)用Child().foo()之后的打印實(shí)際上是:
2
3
它們線(xiàn)性化的到的繼承關(guān)系和前面全是class的代碼并沒(méi)有差別:

從上面的uml圖我們就能理解為什么打印是23了
理解了這個(gè)簡(jiǎn)單的例子之后我們?cè)賮?lái)看一個(gè)復(fù)雜一點(diǎn)的例子:
class Base1 {
void foo1() {
print("Base1.foo1");
foo2();
}
void foo2() {
print("Base1.foo2");
}
}
mixin Base2 on Base1 {
void foo1() {
super.foo1();
print("Base2.foo1");
}
void foo2() {
print("Base2.foo2");
}
}
class Child with Base1,Base2 {}
void main() {
Child().foo1();
}
它的輸出是:
Base1.foo1
Base2.foo2
Base2.foo1
原因是Base2.foo1中的super.foo1實(shí)際上調(diào)用的是Base1.foo1,而B(niǎo)ase1.foo1中的foo2,由于繼承的多態(tài)特性,調(diào)用的是Base2.foo2。我們可以通過(guò)下面uml圖輔助理解,注意看繼承關(guān)系里面是沒(méi)有Base1、Base2的因?yàn)樗鼈兌际峭ㄟ^(guò)with混入的,并不是Child的父類(lèi):

with的類(lèi)不能有構(gòu)造函數(shù)
另外,with的class和mixin類(lèi)型都是不允許有構(gòu)造函數(shù)的,因?yàn)閙ixin機(jī)制語(yǔ)義上是向一個(gè)類(lèi)混入其他類(lèi)的方法或者成員變量,使得我們可以在混合類(lèi)中訪(fǎng)問(wèn)到混入類(lèi)的方法或者屬性。而混入其他類(lèi)的構(gòu)造函數(shù)實(shí)際上是沒(méi)有意義的,因?yàn)椴粫?huì)有人手動(dòng)去調(diào)用這個(gè)混入類(lèi)的構(gòu)造函數(shù)。
class Base1 {
Base1() {}
}
// 編譯失敗: 不能with一個(gè)帶有構(gòu)造函數(shù)的類(lèi)
// class Child with Base1 {}
// 編譯失敗: mixin類(lèi)型只能with,所以不能有構(gòu)造函數(shù)
// mixin Base2 {
// Base2() {}
// }
參考博客
https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3