什么是mixin
mixin是dart语言里的新特性,是定义代码的一种方法,这种代码可以在多个类层级结构中被复用。它是为面向对象程序设计语言中的类提供了方法的实现。其它类不用成为mixin的子类,就可以访问它定义的方法和变量,这样就达到了在多个类层级结构中复用的效果,在类中混入其它的功能,增强代码的复用能力,解决了单继承(dart不支持多继承)带来的代码冗余问题。
如何使用mixin
1. 定义mixin
我们可以使用mixin关键字,定义一个mixin。定义mixin时,不能拥有构造函数以及extends子句和with子句。
例如定义一个名称为 Musical 的mixin,该mixin拥有方法和变量:
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');
}
}
}
2. 定义mixin class
当然,除了使用mixin关键字定义一个mixin之外,我们还可以使用 mixin class (mixin class 的使用,dart版本至少是3.0的版本)关键字定义一个即可以用作常规类,也可以用作mixin的类,但它不能拥有构造函数以及extends子句和with子句。
例如:
mixin class Musician {
// ...
}
class Novice with Musician { // 把 Musician 当作一个 mixin
// ...
}
class Novice extends Musician { // 把 Musician 当作一个 class
// ...
}
3. abstract 抽象关键字
在上面定义的mixin的例子中,都没有出现 abstract 抽象关键字,但mixin是支持定义抽象成员的,例如定义一个抽象方法:
mixin Musician {
void playInstrument(String instrumentName); // 抽象方法.
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
class Virtuoso with Musician {
void playInstrument(String instrumentName) { // 必须要实现该方法
print('Plays the $instrumentName beautifully');
}
}
其中,类Virtuoso 混入了 Musician,所以类Virtuoso必须要实现抽象方法 playInstrument 。
4. implements 接口
既然提到了abstract 抽象关键字,那么在定义mixin时是否支持implements 子句呢?答案是肯定支持 implements 子句的。例如:
abstract interface class Tuner {
void tuneInstrument();
}
mixin Guitarist implements Tuner {
// Guitarist 虽然使用了 implements 子句,但实际上它并没有实现 Tuner 接口
void playSong() {
tuneInstrument();
print('Strums guitar majestically.');
}
}
class PunkRocker with Guitarist {
void tuneInstrument() {
print("Don't bother, being out of tune is punk rock.");
}
}
mixin Guitarist虽然使用了 implements 子句,但实际上它并没有实现Tuner 接口,而是留给类 PunkRocker实现,这一点与在mixin定义abstract 抽象成员一样,都是留给类混入时实现。
5. mixin的继承和superclass约束—— on 子句
我们在定义mixin时,可以使用 on 关键字来达到一种继承关系,on后面可以是mixin,也可以是class。使用了on之后,则表明该mixin只能用于on后面的类的子类,即supperclass约束。例如:
class Musician {
musicianMethod() {
print('Playing music!');
}
}
mixin MusicalPerformer on Musician {
perfomerMethod() {
print('Performing music!');
super.musicianMethod();
}
}
mixin MusicalPerformerA on MusicalPerformer {
@override
perfomerMethod() {
print('PerformingA music!');
super.perfomerMethod();
}
}
class SingerDancer extends Musician with MusicalPerformer { }
定义mixin MusicalPerformer时,加上了 on 子句(即 on Musician)supperclass约束,这里的supperclass是类 Musician ,当有其它类想要使用 MusicalPerformer 时,其它类必须继承类 Musician,类SingerDancer继承了类 Musician,所以类SingerDancer可以with MusicalPerformer。
注意:mixin 可以拥有 on 子句,但是常规类 class 不能用 on 子句,就连mixin class 也不能用 on 子句。
with 关键字
在定义普通类的时候,我们可以使用with关键字并且后面跟上一个或多个mixin的名称,将一个或多个mixin混入到该类中,这样该类就可以使用mixin的方法和变量了,该类也可以重写mixin的方法。
例如:
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 extends Performer with Musical {
@override
void entertainMe() {// 重新了entertainMe方法
print('Musician entertainMe');
}
// ···
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
main() {
Musician().entertainMe();
Maestro('Maestro').entertainMe();
}
Musician类继承Performer,但混入Musical;而Maestro继承Person,但混入Musical, Aggressive和Demented多个mixin。所以类Musician和类Maestro可以使用Musical的方法entertainMe()。
多个mixin同名方法——线性化
当我们with了多个mixin,而这些mixin又拥有相同的方法,类使用mixin的同名方法时,又该如何调用呢?
简单描述为,距离with关键字越远的mixin会重写前面mixin中的相同方法,由于当前类可以重写with的mixin的方法,所以分为两种情况:
(1)当前类没有重写mixin的方法,则会使用距离with最远的那个mixin的方法。
(2)当前类重写了mixin的方法,则会调用当前类重写的方法。
例如:
mixin A{
void printMsg() => print("A");
}
mixin B{
void printMsg() => print("B");
}
class Cl with A, B{}
class Cl1 with A, B{
@override
void printMsg() { // 当前类重写了printMsg方法
print("Cl1");
// super.printMsg();
}
}
void main(){
Cl().printMsg();
Cl1().printMsg();
}
输出的结果是 B 和 Cl1 。
这里思考一个问题,当我们with多个mixin,并且使用了supper调用父类的方法时,supper调用到的方法到底是哪一个类的呢?
例如:
class Supper{
void printMsg() => print("supper");
}
mixin A on Supper{
void printMsg() => print("A");
}
mixin B on Supper{
void printMsg() {
print("B");
super.printMsg(); // 调用的是 A 的方法
}
}
class Cl extends Supper with A, B{
void printMsg() {
print("Cl");
super.printMsg(); // 调用的是 B的方法
}
}
void main(){
Cl().printMsg();
}
输出的结果为:Cl B A,而不是:Cl B Supper。
因为这段代码编译之后,转换为 “A extends Supper”,“B extends A”。所以当我们在使用supper调用父类的方法时,需要多加小心。
嗯,就写到这里了,下期再见!记得点个「在看」
点击👇下方卡片关注公众号,加★星标★