前言

mixin可以簡單的理解為”已經實作的interface”。以下範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
mixin CryAbility{
  void cry() => print("cry very loud");
}

class Baby with CryAbility{
  int age = 0;
}

// 使用時
var baby = Baby();
baby.cry();

是否還蠻好懂得呢?接下來更細緻一點介紹mixin的使用方法。

舊版宣告

mixin在舊版的dart中是直接宣告為class,然後只要在想加上mixin的class中使用with關鍵字,就會自動被解析為mixin。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// abstract 拿掉亦可執行
abstract class CryAbility{
  void cry() => print("cry very loud");
}

class Baby with CryAbility{
  int age = 0;
}

// 使用時
var baby = Baby();
baby.cry();

不過這樣造成語意不清,身為mixin的class應該只能被with,不應該被實體化。
於是多數人會在mixin class前面加上abstract,以提醒使用者。
為了可讀性,還是盡可能使用新版的mixin關鍵字比較好。

多重實作

如同一個class可以實作多個interface,一個class同樣也可以實作多個mixin。以下範例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
mixin CryAbility{
  void cry() => print("cry very loud");
}

mixin EatAbility{
  void eat() => print("eat eat eat");
}

class Baby with CryAbility, EatAbility{
  int age = 0;
}

// 使用時
var baby = Baby();
baby.cry();
baby.eat();

限制實作對象

如果今天想限制只有某些class可以實作mixin,比如說,假如想限制只有Baby可以cry,其他class(例如IronMan)不能cry。那可以用以下的寫法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// on 可以實作的對象
mixin CryAbility on Baby{
  void cry() => print("cry very loud");
  // 可以使用on對象上的物件
  void cryWithAge() => print("A $age year-old baby cry very loud");
}

// ERROR: 注意!!!此處不能with,原因後述。
class Baby {
  int age = 0;
}

// 沒問題,GrowthBaby是Baby的一種
class GrowthBaby extends Baby with CryAbility{
  int age = 1;
}

// ERROR: IronMan不屬於Baby,編譯器會報錯。
class IronMan with CryAbility{
  int iron = 10;
}

// 使用時
var baby = GrowthBaby();
baby.cryWithAge();

當限制CryAbility只能on Baby時,CryAbility就有了存取Baby field的能力。因此cryWithAge才能使用age這個Baby的field。
值得一提的是,IronMan報錯不難理解,但為何無法在原始的Baby class上with CryAbility呢? 這牽扯到了mixin這個關鍵字如何被轉譯成舊版的宣告形式的。以下範例

1
2
3
4
5
6
7
8
mixin CryAbility on Baby{
  void cry() => print("cry very loud");
}

// 等價於
abstract class CryAbility extends Baby{
  void cry() => print("cry very loud");
}

所以為何CryAbility能使用Baby field? 因為他繼承了Baby。
為何Baby不能with CryAbility? 因為他是CryAbility的super class。
這點在使用上要特別小心。

實作同一個方法

如果實作多個mixin,剛好有同樣的method name,那到底誰會被呼叫呢?
簡單結論是: 最後一個被with的會被呼叫。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
mixin CryAbility1{
  void cry() => print("cry1");
}


mixin CryAbility2{
  void cry() => print("cry2");
}

class Baby12 with CryAbility1, CryAbility2{}

class Baby21 with CryAbility2, CryAbility1{}

main(){
  Baby12().cry(); // 輸出: cry2
  Baby21().cry(); // 輸出: cry1
}

更詳細內容請看參考資料: 【译】Dart | 什么是Mixin

Reference