Dart中的Mixin
什么是Mixin
从翻译角度看mixin是混入的意思,从技术的角度mixin是一个没有构造函数的类。
iOS的理解mixin类似于 swift中protocol允许 默认实现。如下:
protocol Fly {
//协议中定义 为提供默认实现
func flyLow()
}
extension Fly {
//对协议进行扩展 提供默认实现
func fly(){
print("fly")
}
}
class Bird : Fly {
//未提供默认实现的类需要实现接口
func flyLow() {
print("fly low")
}
//默认实现的类可以选择不在实现
// func fly() {
//
// }
}
let bird = Bird()
bird.fly()
bird.flyLow()
--------------------------------------------------
fly
fly low
在Android 可以理解为带默认实现的 interface
interface Fly{
public default void fly(){
System.out.println("fly");
}
}
举个例子
我们用经典的Animals举个例子,直接看代码,
这里我们定义了一个抽象类 animal,添加了三种行为,跑步,飞,游泳。
创造了三个小动物,小鸟、小狗、青蛙,都继承与Animal,分别实现了飞、跑,跑和游泳。让我们看看现在的代码群在什么问题?
abstract class Animal {}
class Run {
run() {
print('run');
}
}
class Fly {
fly() {
print('fly');
}
}
class Swim {
swim() {
print('swim');
}
}
class Bird extends Animal implements Fly {
fly() {
print('bird fly');
}
}
class Dog extends Animal implements Run {
//这样写时报错的
@override
run() {
// super.run();
}
}
class Frog extends Animal implements Run, Swim {
run() {
print("run");
}
swim() {
print('swim');
}
}
main(List<String> args) {
Frog frog = Frog();
frog.run();
frog.swim();
}
注:为什么行为类可以直接当做接口实现,是因为dart类创建的时候就添加了一个隐式接口类,可用来实现用。
目前的表象
第一、小动物类中,复写了行为类方法后,不能使用super调用,因为默认super找的是animal中的方法。
第二、如果要实现就需要在小动物类中将需要实现的方法全部重新实现。
这样对比swift、java当中协议的、接口的默认实现,就略显不灵活了,尤其在多个类层次结构中重用类的代码的方法时候就很麻烦了。dart使用了另外一种方式解决这个问题,那就是mixin。让我们对上述代码进行改造下。
abstract class Animal {}
mixin Run {
run() {
print('run');
}
}
mixin Fly {
fly() {
print('fly');
}
}
//mixin使用 class也是可以的
class Swim {
swim() {
print('swim');
}
}
class Bird extends Animal with Fly {}
class Dog extends Animal with Run {}
class Frog extends Animal with Swim, Run {}
main(List<String> args) {
Bird bird = Bird();
bird.fly();
Dog dog = Dog();
dog.run();
Frog frog = Frog();
frog.run();
frog.swim();
}
注:如果 Mixin 不希望作为常规类被使用,使用关键字 mixin 替换 class。
这样改造后小动物们不用在重新复写行为方法,直接调用即可。
多继承
dart中多继承有两种方式:
第一种 implement 多个接口类,但是必须要实现类中所有方法与变量。
第二种,就是使用上边的mixin。
特性
mixin有一个特性,那就是线性化,上边的例子中如果每个行为的函数都一样,类似于下边这种:
class A {
String getMessage() => 'A';
}
class B {
String getMessage() => 'B';
}
class P {
String getMessage() => 'P';
}
class AB extends P with A, B {}
class BA extends P with B, A {}
void main() {
String result = '';
AB ab = AB();
result += ab.getMessage();
BA ba = BA();
result += ba.getMessage();
print(result);
}
这个会输出什么结果呢,会输出如下结果
BA
大家可能想会想到,这个跟mixin的顺序有关系,最后混入的 mixin 的函数,被调用了。这说明最后一个混入的 mixins 会覆盖前面一个 mixins 的特性。
这里引出一个概念线性化,声明 mixin 的顺序代表了继承链的继承顺序,声明在后面的 mixin,一般会最先执行。
实际上这段代码
class AB extends P with A, B {}
class BA extends P with B, A {}
在语义上等同于
class PA extends P with A;
class PAB extends PA with B;
class AB extends PAB {}
class PB extends P with B;
class PBA extends PB with A;
class BA extends PBA {}
最终的继承关系如下图
可有看出了这是一个线性的继承关系,dart在P与AB、BA中创建了中间类来做方法混入,这就能解释了为什么调用的时候都是顺序加入最后的一个了。
mixin的使用场景
至此,大家一起探讨一下,根据这个mixin的特性,在工程中那些方面可以使用?