Dart之Mixin詳解

隨着Dart學習的深刻,發現了一個比較棘手的語法——mixin。它對於Java開發人員來講,是一個全新的概念,但也是深刻學習Dart沒法繞過的一個檻。java

那麼mixin究竟是什麼尼???下面來看維基百科對它的定義。編程

mixin是面向對象程序設計語言中的類,提供了方法的實現。其餘類能夠訪問mixin類的方法、變量而沒必要成爲其子類。編程語言

簡而言之,mixins是普通的類,咱們能夠從中擴展方法(或變量)而不擴展類。ide

應用場景

首先咱們來看一個應用場景。學習

圖片來自於 Dart: What are mixins?

在上圖中,有一個類——Animal,它有三個子類——MammalBirdFish,而這三個類也有其對應的子類。ui

下面分別經過JavaDart來實現上面這些類及其繼承關係。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 {...}
複製代碼

代碼幾乎一摸同樣,下面就根據上圖來分別給這些類添加行爲——WalkSwimFlying。因爲這些行爲並非全部類通用的,因此不能將這些行爲放在父類。但若是把這三個行爲分別放在三個類中,而後讓其餘類來繼承這三個類,也就能夠解決上述問題。但這樣就是多繼承,而JavaDart又不支持多繼承。因此這時候凸顯出接口的重要性,經過接口來實現上述行爲。代碼以下: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替換成了withabstract class替換成了mixin嘛,也太簡單了。但仔細一看,咱們發現mixin裏有方法的具體實現,這樣能夠避免接口的方法必須在子類實現從而致使的代碼冗餘(Java 8經過關鍵字default也能夠作到這一點)問題。簡而言之,mixin相對於接口可以更好的避免代碼冗餘,使代碼更加集中。

其實筆者之因此學習mixin,是由於在flutter源碼中大量使用了mixin,而若是不瞭解mixin的話,就沒法分析flutter源碼。哈哈哈......

mixin之線性化

在上面的示例中,咱們發現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爲主。那麼結果應該以下。

但其實真實結果其實以下。

這是爲何尼?仔細一點就能夠發現,咱們在 WidgetsBindingRendererBindingGestureBinding中都調用了父類的 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過程當中遇到的幾個問題。

注意一

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後加上類GestureBindingRendererBinding,則必須GestureBindingRendererBinding的前面,不然會報錯。

當咱們讓 GestureBindingRendererBinding前面後,該錯誤就會消失。

Dart版本較低時,是沒有mixinon關鍵字的......

【參考資料】

《Dart編程語言》

理解Dart的Mixin繼承機制

Dart for Flutter : Mixins in Dart

Dart: What are mixins?

相關文章
相關標籤/搜索