Home dart 中的 mixin
Post
Cancel

dart 中的 mixin

本文讨论基于 dart 2.2 环境

学过 C++ 的同学都知道,C++ 允许多重继承,在某些情况下这种能力是非常有用的。而 Dart 语言不支持多重继承,但提供了一个类似的能力:mixins 。 mixin 类似于类,但它的定位是:定义某些行为,这些行为可以同时为其他类复用。但这些类又不需要使用多重继承。那它是怎么实现的呢?其实可以通过文档来找答案:官方文档:Dart 2 Mixin Declarations

不过,本文想更直观点,从代码一步步找到答案:假设有以下代码 类D继承于 C,同时希望具备 A,B 的能力。正常情况下,A,B,C 应该各有不同的可复用的方法,这里选择ABC都有同一个方法这种情况来验证,当extend 和 with 所指向的类有方法冲突时,dart 怎么办


测试1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mixin A{
    foo(){
        print('A');
    }
}
mixin B{
    foo(){
        print('B');
    }
}
class C{
    foo(){
        print('C');
    }
}
class D extends C with B,A{
//    foo(){
//        print('D');
//    }
// 如果 D 方法里也有 foo 方法,则显示 D.
}
main(){
    D().foo(); // 显示 'A'
}

以上代码可以得到两种猜测:

当extend 和 with 所指向的类有方法冲突时

  • 保留最右边的
  • 优先调用最右边的

不管上面是哪个结论其实可以得到一个结论:父类和mixin类中有方法相同时,右边优先。子类如果本身也有此方法,则优先使用子类的。


测试2

mixin 还有一个用法,可以指定mixin 只能用于哪些类:

1
2
3
mixin SomethingOnlyCanBeMixinWithOther on Other{

}

上面这个类只能被 mixin 到 Other 类里,其他类要想用则会报错

1
2
class Other with SomethingOnlyCanBeMixinWithOther{} /// 没问题
class AnOther with SomethingOnlyCanBeMixinWithOther{} /// 编译错误

新的测试代码,添加 on C, 并在两个 mixin 里添加 super.foo()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mixin A on C{
    foo(){
        print('A');
        super.foo();
    }
}
mixin B on C{
    foo(){
        print('B');
        super.foo();
    }
}
class C{
    foo(){
        print('C');
    }
}
class D extends C with B,A{
//     foo(){
//         print('D');
//     }
}


main(){
    D().foo(); // 输出 A B C
}

为了更直观,特意把 super 放到的 print 后面,输出结果即为执行的顺序。可见在用 on 时上一测试中判断2是对的。 而从 super 的调用来看,可以得到这样的一个推论:

class D 继承了一个类 Ax 而 Ax 继承了一个 Bx ,Bx 继承了 Cx。这些中间的类,是dart 编译期实现(也可能并没真正存在),我们并不能在代码上直接找到 Cx Bx Ax 这些类。

如果以上推论正确,那其实 mixin 在某种意义上也可以理解为一种语法糖


测试3

on 允许有多个并行,这个好玩了,定义 A 只能应用于同时兼容 C 和 E 的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
mixin E{
  foo(){
    print("E");
  }
}

mixin A on C,E{ // **********
    foo(){
        print('A');
        super.foo();
    }
}
mixin B on C{
    foo(){
        print('B');
        super.foo();
    }
}
class C{
    foo(){
        print('C');
    }
}
class D extends C with B,A,E{ // ****** 注意顺序
}


main(){
    D().foo();
}

以上代码在 dartPad 上执行的错误如下:

Error: '_D&C&B' doesn't implement 'E' so it can't be used with 'A'.

‘_D&C&B’ 应该就是所生成的中间类 了,它想使用 A 时,发现并不满足 A 的约束条件 : on C, E 。所以上面的结论是正确的。

调整一下 with 的顺序 为 B,E,A : 可看到输出为 :A,E 因为 E 没有依赖,所以语法上无法调用 super(尽管实际上生成了一个中间类 Ex 是有 super 的。),那到此就断开了。

而顺序为 E,B,A 时输出则为 A,B,E。

由此可见,多个 on 时,需要注意调用链问题,解决问题的方法是尽量用有共同约束的 mixin ,或调用 with 顺序。

This post is licensed under CC BY 4.0 by the author.