Mixinsとは何か
Mixinはコードの再利用手法の一つで、関連のないクラスの継承階層に対して共通コードを導入できます。目的は複数のクラスに一括してメンバー実装を提供することです。多重継承に似ていますが、クラスにモジュール的な機能を追加するために使われます。
Mixinの利用方法
withキーワードの後に1つ以上のMixin名を記述します。
class Musician extends Performer with Musical {
// コード
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
Code language: Dart (dart)
Mixinを定義する
mixinキーワードで宣言します。
補足:
mixin class(Dart 3.0以降)は通常のクラスとMixin、両方として利用可能です。
制約事項
mixin/mixin classではextendsを記述できない- 生成コンストラクタ(generative constructor)を定義してはならない
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
class Musician with Musical {}
void main() {
var m = Musician();
m.canConduct = true;
m.entertainMe(); // Waving hands
}
Code language: Dart (dart)
実行結果
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingCode language: CSS (css)
Mixinが外部のメンバーを呼び出す場合
Mixin自身で実装できないメソッド・プロパティ
Mixinは自身に実装のないメンバーを呼び出す必要がよくあります。代表的な4つの手法を紹介します。
Mixin内に抽象メンバーを定義
Mixinに抽象メソッドを定義し、このMixinを利用するすべてのクラスは抽象メソッドを実装する必要があります。
mixin Musician {
// 抽象メソッド。利用側が実装しなければならない
void playInstrument(String instrumentName);
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
// 利用クラスはオーバーライドして実装
class Virtuoso with Musician {
@override
void playInstrument(String instrumentName) {
print('Plays the $instrumentName beautifully');
}
}
void main() {
var artist = Virtuoso();
artist.playPiano();
artist.playFlute();
}
Code language: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully
抽象Getterでホストクラスの状態にアクセス
Mixinに抽象getterを定義し、ホストクラスのフィールドを読み取ることで、クラスをまたいだ状態参照を実現します。
/// nameプロパティを持つクラスに混入可能。nameに基づく==とhashCodeを自動提供
mixin NameIdentity {
String get name;
@override
int get hashCode => name.hashCode;
@override
bool operator ==(Object other) =>
other is NameIdentity && name == other.name;
}
class Person with NameIdentity {
final String name;
Person(this.name);
}
void main() {
var p1 = Person("Alice");
var p2 = Person("Alice");
var p3 = Person("Bob");
print(p1 == p2); // true
print(p1 == p3); // false
}
Code language: JavaScript (javascript)
implementsによるインターフェース実装Mixin
Mixinでimplementsを使ってインターフェースを制約し、混入先クラスにインターフェースのすべてのメソッド実装を強制します。
abstract interface class Tuner {
void tuneInstrument();
}
mixin Guitarist implements Tuner {
void playSong() {
tuneInstrument();
print('Strums guitar majestically.');
}
}
class PunkRocker with Guitarist {
@override
void tuneInstrument() {
print("Don't bother, being out of tune is punk rock.");
}
}
void main() {
PunkRocker().playSong();
}
Code language: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.
on句:特定親クラスのサブクラスだけにMixinの利用を制限
onの役割:指定した型を継承したクラスのみ、このMixinを利用できるように制限します。
同時にMixin内部から親クラスのメンバーを呼び出し、super.xxx()を実行可能になります。
Mixin内で
superによる親メソッド呼び出しが必要な場合に限り、onを使用してください!
class Musician {
void musicianMethod() {
print('Playing music!');
}
}
// Musicianのサブクラスのみ混入可能
mixin MusicalPerformer on Musician {
void performerMethod() {
print('Performing music!');
super.musicianMethod(); // onで制約された親クラスメソッドを呼び出し
}
}
// SingerDancerはMusicianを継承しているのでMusicalPerformerを混入可能
class SingerDancer extends Musician with MusicalPerformer {}
void main() {
SingerDancer().performerMethod();
}
Code language: Dart (dart)
Performing music!
Playing music!
SingerDancerはMusicalPerformerを混入しているため、内部のperformerMethodを利用できます
混入の仕組みはこの通りです。クラスは別のMixinに定義されたメソッドを自身に取り込んで使用できます。
⚠️ 誤った例:
クラスが
Musicianを継承していない状態で直接with MusicalPerformerを記述するとコンパイルエラーとなります。
class / mixin / mixin class の違い
mixin:withでのみ利用可、extendsによる継承は不可class:extendsが使用可能、直接Mixinとしては使えないmixin class:withを使ってMixinとして利用可能extendsを使って通常の親クラスとしても利用可能
制約は共通で適用されます:
- mixin / mixin class extends、with を記述できない
- class / mixin class on は使用不可
mixin class Musician { void play() { print("Musician is playing"); } } // Mixinとして使用 class Novice1 with Musician {} // 通常の親クラスとして継承 class Novice2 extends Musician {} void main() { Novice1().play(); Novice2().play(); }Code language: Dart (dart)実行結果
D:\dartdemo\firstdart>dart run Building package executable... Built firstdart:firstdart. Musician is playing Musician is playingCode language: CSS (css)Mixins
Previous: クラスの継承とメンバーのオーバーライドNext: 列挙型