Mixinについて大分混乱したのでメモ。
Dartのクラス
Dartのクラスには2種類ある。
- 普通のクラス(
class
) - 抽象クラス(
abstract class
)
抽象クラスは全てのメソッドに実装を与える必要がないが、newによるインスタンス生成ができない。
Dartの継承
Dartの継承には2種類ある。
-
extends
による継承 -
implements
による継承
extends
は継承元のインターフェースと実装を全て継承先に引き継ぐ。普通継承といえばこれ。あるクラスがextends
できるのは一つのクラスだけ。
implements
は継承元のインターフェースを全て継承先に引き継ぐ。継承先からは継承元の実装を使うことができない。あるクラスから複数のクラスをimplements
できる。
どちらも継承元の型を継承先の型で置き換えることができる。つまり、継承先の型は継承元の型のサブクラスになっている。
Mixin
DartにはMixin(クラスの合成)という機構がある。
Mixinは指定したクラスからインターフェースと実装を引き継ぐ(extends
による継承と同じ)。
extends
と異なるのは、あるクラスから複数のクラスをMixinできるということ。
しかし、Mixinされる(インターフェースと実装を提供する)側のクラスはObject以外から実装を継承することができない。
ここがミソで、Mixinされるクラスでもインターフェースはいくらでも継承できる。
したがって、Mixinされることを目的としたクラスで(型の観点から)親クラスを持ちたい場合はimplements
による継承をすれば良い。
class A {
void hoge() => print('A');
}
// BはAのインターフェースのみを継承する
abstract class B implements A {
void foo() => print('foo');
}
// CはBの実装とインターフェースを継承するが、
// BではA.hogeの実装が与えられていないので
// Cでhogeの実装を与える必要がある。
class C extends Object with B {
void hoge() => print('C');
}
main() {
C c = new C();
c.hoge(); // => 'C' Aの実装は使えない
c.foo(); // => 'foo' Bの実装は使える
A a = c; // CはAのサブクラスとして扱われる
B b = c; // CはBのサブクラスとして扱われる
}