隨着Dart
學習的深刻,發現了一個比較棘手的語法——mixin
。它對於Java
開發人員來講,是一個全新的概念,但也是深刻學習Dart
沒法繞過的一個檻。java
那麼mixin
究竟是什麼尼???下面來看維基百科對它的定義。編程
mixin
是面向對象程序設計語言中的類,提供了方法的實現。其餘類能夠訪問mixin
類的方法、變量而沒必要成爲其子類。編程語言
簡而言之,mixins
是普通的類,咱們能夠從中擴展方法(或變量)而不擴展類。ide
首先咱們來看一個應用場景。學習
在上圖中,有一個類——Animal
,它有三個子類——Mammal
、Bird
及Fish
,而這三個類也有其對應的子類。ui
下面分別經過Java
及Dart
來實現上面這些類及其繼承關係。spa
Java
代碼.net
//Java代碼
public class Animal {...}
public class Mammal extends Animal {...}
public class Bird extends Animal {...}
public class Fish extends Animal {...}
public class Dolphin extends Mammal {...}
public class Bat extends Mammal {...}
public class Cat extends Mammal {...}
public class Dove extends Bird {...}
public class Duck extends Bird {...}
public class Shark extends Fish {...}
public class FlyingFish extends Fish {...}
複製代碼
Dart
代碼設計
//Dart代碼
class Animal {...}
class Mammal extends Animal {...}
class Bird extends Animal {...}
class Fish extends Animal {...}
class Dolphin extends Mammal {...}
class Bat extends Mammal {...}
class Cat extends Mammal {...}
class Dove extends Bird {...}
class Duck extends Bird {...}
class Shark extends Fish {...}
class FlyingFish extends Fish {...}
複製代碼
代碼幾乎一摸同樣,下面就根據上圖來分別給這些類添加行爲——Walk
,Swim
及Flying
。因爲這些行爲並非全部類通用的,因此不能將這些行爲放在父類。但若是把這三個行爲分別放在三個類中,而後讓其餘類來繼承這三個類,也就能夠解決上述問題。但這樣就是多繼承,而Java
及Dart
又不支持多繼承。因此這時候凸顯出接口的重要性,經過接口來實現上述行爲。代碼以下:3d
//行走行爲
public interface Walk {
void walk();
}
//游泳行爲
public interface Swim {
void swim();
}
//飛翔行爲
public interface Flying {
void flying();
}
//海豚能夠游泳
public class Dolphin extends Mammal implements Swim {
@Override
public void swim() {...}
}
//蝙蝠能夠飛、行走
public class Bat extends Mammal implements Flying,Walk {
@Override
public void walk() {...}
@Override
public void flying() {...}
}
//貓能夠行走
public class Cat extends Mammal implements Walk {
@Override
public void walk() {...}
}
//鴿子能夠行走、飛
public class Dove extends Bird implements Walk,Flying {
@Override
public void walk() {...}
@Override
public void flying() {...}
}
//鴨子能夠行走、飛及游泳
public class Duck extends Bird implements Walk,Flying,Swim {
@Override
public void swim() {...}
@Override
public void walk() {...}
@Override
public void flying() {...}
}
//鯊魚能夠游泳
public class Shark extends Fish implements Swim {
@Override
public void swim() {...}
}
//飛魚能夠游泳、飛
public class FlyingFish extends Fish implements Swim,Flying {
@Override
public void swim() {...}
@Override
public void flying() {...}
}
複製代碼
在Java
中經過接口給類添加了行爲。下面就開始在Dart
中給類添加行爲,雖然Dart
中沒有interface
關鍵字,但Dart
中是有接口的概念且任意類均可以做爲接口。代碼以下:
//行走行爲
abstract class Walk{
walk();
}
//游泳行爲
abstract class Swim{
swim();
}
//飛翔行爲
abstract class FLying {
flying();
}
//海豚能夠游泳
class Dolphin extends Mammal implements Swim {
@Override
public void swim() {...}
}
//蝙蝠能夠飛、行走
class Bat extends Mammal implements Flying,Walk {
@Override
void walk() {...}
@Override
void flying() {...}
}
//貓能夠行走
class Cat extends Mammal implements Walk {
@Override
void walk() {...}
}
//鴿子能夠行走、飛
class Dove extends Bird implements Walk,Flying {
@Override
void walk() {...}
@Override
void flying() {...}
}
//鴨子能夠行走、飛及游泳
class Duck extends Bird implements Walk,Flying,Swim {
@Override
void swim() {...}
@Override
void walk() {...}
@Override
void flying() {...}
}
//鯊魚能夠游泳
class Shark extends Fish implements Swim {
@Override
void swim() {...}
}
//飛魚能夠游泳、飛
class FlyingFish extends Fish implements Swim,Flying {
@Override
void swim() {...}
@Override
void flying() {...}
}
複製代碼
代碼幾乎與Java
實現一摸同樣。但在Dart
中,咱們還能夠用Mixin
來實現上述需求。經過Mixin
將上面的一些行爲加入到各自對應的類中。下面來代碼實現:
//行走
mixin Walker{
void walk(){...}
}
//游泳行爲
mixin Swim{
void swim(){...}
}
//飛翔行爲,因爲這個是抽象方法,因此必須在要實現,不能調用super.flying()
mixin Flying {
void flying();
}
//海豚能夠游泳
class Dolphin extends Mammal with Swim{
@override
void swim() {
super.swim();
}
}
//蝙蝠能夠飛、行走
class Bat extends Mammal with Flying,Walk{
@override
void flying() {...}
//覆蓋Walk類中的walk方法
@override
void walk() {
super.walk();
}
}
//貓能夠行走,這裏沒有重寫Walk中的方法
class Cat extends Mammal with Walk{}
//鴿子能夠行走、飛
class Dove extends Bird with Flying,Walk{
@override
void flying() {...}
}
//鴨子能夠行走、飛及游泳
class Duck extends Bird with Walk,Flying,Swim{
@override
void flying() {...}
@override
void walk() {...}
}
//鯊魚能夠游泳
class Shark extends Fish with Swim{...}
//飛魚能夠飛及游泳
class FlyingFish extends Fish with Flying,Swim{
@override
void flying() {...}
}
複製代碼
咋一看,這不就是將implement
替換成了with
,abstract class
替換成了mixin
嘛,也太簡單了。但仔細一看,咱們發現mixin
裏有方法的具體實現,這樣能夠避免接口的方法必須在子類實現從而致使的代碼冗餘(Java 8
經過關鍵字default
也能夠作到這一點)問題。簡而言之,mixin
相對於接口可以更好的避免代碼冗餘,使代碼更加集中。
其實筆者之因此學習mixin
,是由於在flutter
源碼中大量使用了mixin
,而若是不瞭解mixin
的話,就沒法分析flutter
源碼。哈哈哈......
在上面的示例中,咱們發現with
關鍵字後有多個類。那麼這裏就產生了一個問題——若是with
後的多個類中有相同的方法,那麼當調用該方法時,會調用哪一個類裏的方法尼?因爲距離with
關鍵字越遠的類會重寫前面類中的相同方法,所以分爲如下兩種狀況
with
關鍵字最遠類中的方法。下面來看一個示例。
//BindingBase
abstract class BindingBase {
void initInstances() {
print("BindingBase——initInstances");
}
}
//GestureBinding
mixin GestureBinding on BindingBase{
@override
void initInstances() {
print("GestureBinding——initInstances");
super.initInstances();
}
}
//RendererBinding
mixin RendererBinding{
@override
void initInstances() {
print("RendererBinding——initInstances");
super.initInstances();
}
}
// WidgetsBinding
mixin WidgetsBinding on BindingBase
{
@override
void initInstances() {
print("WidgetsBinding——initInstances");
super.initInstances();
}
}
//WidgetsFlutterBinding
class WidgetsFlutterBinding extends BindingBase with GestureBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
return WidgetsFlutterBinding();
}
}
//main
main(List<String> arguments) {
var binding = WidgetsFlutterBinding();
binding.initInstances();
}
複製代碼
WidgetsFlutterBinding
中並無重寫initInstances
方法,那麼就以最右邊重寫該方法的類——WidgetsBinding
爲主。那麼結果應該以下。
但其實真實結果其實以下。
WidgetsBinding
、
RendererBinding
及
GestureBinding
中都調用了父類的
initInstances
方法,也所以會一級一級往上調用。若是咱們取消該句代碼,則會終止這種調用方式。
好比作以下修改。
mixin WidgetsBinding on BindingBase
{
@override
void initInstances() {
print("WidgetsBinding——initInstances");
}
}
複製代碼
其餘代碼保持不變,這樣就是咱們前面預想的結果了。
最後在用Java
代碼展現一下上面的繼承關係。
public class BindingBase {
void initInstances() {
System.out.printf("BindingBase——initInstances");
}
}
public class GestureBinding extends BindingBase {
@Override
void initInstances() {
// super.initInstances();
}
}
public class RendererBinding extends GestureBinding {
@Override
void initInstances() {
// super.initInstances();
}
}
public class WidgetsBinding extends RendererBinding {
@Override
void initInstances() {
// super.initInstances();
}
}
public class WidgetsFlutterBinding extends WidgetsBinding {
}
複製代碼
主要是說明一下在學習mixin
過程當中遇到的幾個問題。
mixin
類要麼是直接繼承Object
,要麼是直接或間接繼承extends
關鍵字後的類。在前面的示例上作一些修改,以下。
//一個新類
abstract class Binding {
void initInstances() {
print("Binding——initInstances");
}
}
//讓GestureBinding類繼承自Binding
mixin GestureBinding on Binding {
@Override
void initInstances() {
// super.initInstances();
}
}
複製代碼
這時候咱們就會發現代碼報錯,出現了以下警告。
GestureBinding
繼承自
BindingBase
時,上面錯誤就消失了。因此咱們要清楚
mixin
究竟是繼承那個類,不然就可能會出現上述錯誤。
若是類A實現了接口C,類B繼承了接口C,那麼類B必定得在類A的後面。
//接口
abstract class HitTestable {
void hitTest(String msg);
}
//實現接口HitTestable
mixin GestureBinding on BindingBase implements HitTestable{
@override
void initInstances() {
print("GestureBinding——initInstances");
super.initInstances();
}
@override
void hitTest(String msg) {
print("GestureBinding——hitTest:${msg}");
}
}
mixin RendererBinding on BindingBase,GestureBinding,HitTestable{
@override
void hitTest(String msg) {
print("RendererBinding——hitTest:${msg}");
super.hitTest(msg);
}
}
複製代碼
那麼若是想要在with
後加上類GestureBinding
及RendererBinding
,則必須GestureBinding
在RendererBinding
的前面,不然會報錯。
GestureBinding
在
RendererBinding
前面後,該錯誤就會消失。
當Dart
版本較低時,是沒有mixin
及on
關鍵字的......
【參考資料】
《Dart編程語言》