LoginSignup
18
10

More than 5 years have passed since last update.

DartのMixinsとオーバーライド

Posted at

DartのMixinについておさらい

「多重継承のようなもの」を実現するための仕組み。その名の通りクラスを混ぜるようなイメージで、親子関係ではない。

Mixinを使うにはwithを使います。Mixinは継承と同時にしか行えないので、Mixinだけをしたい場合はextends Object withのように、Objectを明示的に継承します。

簡単なサンプル

class Mixin {
  void foo() {
    print("foo on Mixin");
  }
}

abstract class SomeBase {
  void bar() {
    print("bar on Base");
  }
}

class SomeClass extends SomeBase with Mixin {
}

void main() {
  var some = new SomeClass();
  some.foo();
  some.bar();
}

SomeClassでは何の宣言もしていないがMixinのfooと継承元のbarが呼び出せるようになっています。

foo on Mixin
bar on Base

任意のクラスをMixinしているかどうかは継承と同様にisで判定できます。

...
void main() {
  var some = new SomeClass();
  print(some is SomeClass);
  print(some is SomeBase);
  print(some is Mixin);
}
true
true
true

オーバーライドはMixinが優先される

上の例で、SomeBaseにもfooメソッドがあったらどうなるでしょうか

class Mixin {
  void foo() {
    print("foo on Mixin");
  }
}

abstract class SomeBase {
  void foo() {
    print("foo on Base");
  }
}

...

このように継承元とMixinに同じ名前のメソッドがある場合はMixinのほうが優先されます。

foo on Mixin

ちなみに、継承元とMixinのfooメソッドの引数が違う場合も引数に関係なくMixin側が優先されるので、次のコードはエラーとなります。

class Mixin {
  void foo(arg) {
    print("foo on Mixin");
  }
}

...

void main() {
  var some = new SomeClass();
  some.foo();
}
Unhandled exception:
Class 'SomeClass' has no instance method 'foo' with matching arguments.

NoSuchMethodError: incorrect number of arguments passed to method named 'foo'
Receiver: Instance of 'SomeClass'
Tried calling: foo()
Found: foo(arg)
...

継承とMixinではMixinが優先されますが、具象クラスにもfooがある場合は当然そちらが優先されます。

...

class SomeClass extends SomeBase with Mixin {
  void foo() {
    print("foo on Class");
  }
}

...
foo on Class

superの参照先

オーバーライドの優先順位からもわかるように、superの参照先も優先順位はMixinが先です。

...

class SomeClass extends SomeBase with Mixin {
  void foo() {
    print("foo on Class");
    super.foo();
  }
}

...

この場合はまずSomeClassのfooが呼ばれ、その中でsuperによってMixinのfooが呼び出されます

foo on Class
foo on Mixin

必ずMixinを参照するわけではなく、優先順位が上というだけなので、Mixinには無いメソッドはSomeBaseから探されます。ただしコンストラクタの場合は、「Mixinは明示的コンストラクタを持ってはいけない」という規則があるので確実に継承元のコンストラクタが呼び出されます。

...

abstract class SomeBase {
  SomeBase() {
    print("base constractor");
  }
  ...  
}

class SomeClass extends SomeBase with Mixin {
  SomeClass(): super();
  ...
}
...
base constractor
foo on Class
foo on Mixin

複数Mixinは後ろが優先

Mixinは継承と違っていくつでも追加することができますが、この際のオーバーライドの優先順位は「後ろに書かれたものが優先」です。

class Mixin2 {
  void foo() {
    print("foo on Mixin2");
  }
}

class Mixin1 {
  void foo() {
    print("foo on Mixin1");
  }
}
...

class SomeClass extends SomeBase with Mixin1, Mixin2 {
  ...
}
...
foo on Class
foo on Mixin2

withの書き方によって挙動が変わってしまうので、内部のメソッド衝突には気をつけましょう。

まとめ

  • オーバーライドの優先順位は そのクラス自身 > Mixin > 継承元
  • Mixinの中では後ろのほうが優先される

Mixinについては上記の内容も含めその利点など公式ドキュメントにまとめられています。
https://www.dartlang.org/articles/mixins/

18
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
10