上篇文章, 我们学习了Dart基础语法中的单继承关键字abstract、extends关键字
这篇文章, 我们一起来学习下, Dart基础语法中的mixin关键字。
Mixins
官方文档解释:
- Mixin 是一种在多重继承中复用某个类中代码的方法模式。
- 使用
with关键字并在其后跟上 Mixin 类的名字来使用 Mixin 模式 - 想要实现一个
mixin, 那么, 这个类必须使用关键字mixin, 必须继承Object, 并且未声明构造函数 - 可以使用关键字
on来指定哪些类可以使用该Mixin类
那么, Mixin 使用场景是什么? Mixin 具体如何使用?下面, 我们以一个官方例子说明下。
在上图中,有一个类——Animal,它有三个子类——Mammal、Bird及Fish,而这三个类也有其对应的子类。
上面的关系图, 用Dart代码实现
/// 声明Animal基类
abstract Animal {...}
/// 哺乳动物类-->Animal
class Mammal extends Animal {...}
/// 鸟类-->Animal
class Bird extends Animal {...}
/// 鱼类--Animal
class Fish extends Animal {...}
/// 海豚-->哺乳动物类
class Dolphin extends Mammal {...}
/// 蝙蝠-->哺乳动物类
class Bat extends Mammal {...}
/// 猫-->哺乳动物类
class Cat extends Mammal {...}
/// 鸽子-->鸟类
class Dove extends Bird {...}
/// 鸭子-->鸟类
class Duck extends Bird {...}
/// 鲨鱼-->鱼类
class Shark extends Fish {...}
/// 飞鱼-->鱼类
class FlyingFish extends Fish {...}
下面就根据上图来分别给这些类添加行为——Walk,Swim及Flying。由于这些行为并不是所有类通用的,所以不能将这些行为放在父类。因为Dart是单继承的, 没有多继承。所以, 我们可以用Mixin来实现上述需求。通过Mixin将上面的一些行为加入到各自对应的类中。
//行走
mixin Walker {
void walk(){...}
}
//游泳行为
mixin Swim {
void swim(){...}
}
//飞翔行为,由于这个是抽象方法,所以必须在要实现,不能调用super.flying()
mixin Flying {
void flying();
}
//海豚可以游泳
class Dolphin extends Mammal with Swim {
@override
void swim() {
super.swim();
}
}
//蝙蝠可以飞、行走
class Bat extends Mammal with Flying, Walk {
@override
void flying() {...}
//覆盖Walk类中的walk方法
@override
void walk() {
super.walk();
}
}
//猫可以行走,这里没有重写Walk中的方法
class Cat extends Mammal with Walk{}
//鸽子可以行走、飞
class Dove extends Bird with Flying,Walk{
@override
void flying() {...}
}
//鸭子可以行走、飞及游泳
class Duck extends Bird with Walk,Flying,Swim{
@override
void flying() {...}
@override
void walk() {...}
}
//鲨鱼可以游泳
class Shark extends Fish with Swim{...}
//飞鱼可以飞及游泳
class FlyingFish extends Fish with Flying,Swim{
@override
void flying() {...}
}
我们发现mixin里有方法的具体实现,这样可以避免接口的方法必须在子类实现从而导致的代码冗余。
mixin之线性化
在上面的示例中,我们发现with关键字后有多个类。那么这里就产生了一个问题——如果with后的多个类中有相同的方法,那么当调用该方法时,会调用哪个类里的方法? 由于距离with关键字越远的类会重写前面类中的相同方法,因此分为以下两种情况
class A {
printMessage() => print('A');
}
mixin B on A {
printMessage() {
super.printMessage();
print('B');
}
}
mixin C on B {
printMessage() {
print('C0');
super.printMessage();
print('C1');
}
}
class D with A, B, C {
printMessage() {
print('D0');
super.printMessage();
print('D1');
};
}
void main() {
D().printMessage();
}
输出结果:
D0
C0
A
B
C1
D1
由上面程序可知, 当对象D调用printMessage方法时, 先执行的是类D中的方法。再执行super.printMessage();, 因为类D使用with关键字混合Mixins了A, B, C。并且A, B, C类中都有printMessage()方法, 那么, 当在D中执行super.printMessage();时, 程序会先执行A, B, C中的哪个类的printMessage()方法呢?从程序的输出结果, 很清晰地看到, 程序执行顺序是A-->B-->C--D。也就是, 当with混合多个mixin时, 程序先从最后面的mixin类开始执行。
我们得出重要的结论:
- with 后面的类会覆盖前面的类的同名方法
- 为了不影响同一方法的使用,with后面的类的同名方法都要调用
super.printMessage();方法。 - 如果当前使用类重写了该方法,就会调用当前类中的方法。
- 如果当前使用类没有重写了该方法,则会调用距离
with关键字最远类中的方法。