Scala 特质(trait) 的 super 方法调用是动态绑定的
在 Java 或者 Scala 的类中,super.foo() 这样的方法调用是静态绑定的,也就是说当你在代码中写下 super.foo() 的时候就能明确是调用它的父类的 foo() 方法。然而,如果是在特质中写下了 super.foo() 时,它的调用是动态绑定的。调用的实现奖在每一次特质被混入到具体类的时候才被决定。
确切的讲,特质的 super 调用与混入的次序很重要,参照下面的例子说话:
直截的讲就是超靠近后面的特质越优先起作用。当你调用带混入的类的方法是,最右侧特质的方法首先被调用。如果那个方法调用了 super,它调用其左侧特质的方法。可以这么认为,Doubling 的 super 指向了 Incrementing,Incrementing 的 super 指向了 BasicIntQueue。
来看个完整的实例实际体验一把,如果要帮助理解,最好应该实际运行一下这个实例
实例中两次声明 queue1 和 queue2 是采用了不同的顺序混入 Incrementing 和 Doubling 两个特质,执行的效果是不一样,输出分别是 5 和 6。这种对 super 的重新理解会带来冲击,同时很有可能在代码维护时调整混入的特质时引来些许的麻烦。
混入特质存在着一种线性化的次序关系,再来看一下这个线性化的例子:
下图是 Cat 类的继承层级和线性化次序的展示图
继承次序使用白色三角箭头表示,箭头指向超类,黑底箭头说明线性化次序,箭头指向 super 调用解决的方向。
永久链接 https://yanbin.blog/scala-trait-super-dynamic-binding/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
确切的讲,特质的 super 调用与混入的次序很重要,参照下面的例子说话:
1val queue = (new BasicIntQueue with Incrementing with Doubling)直截的讲就是超靠近后面的特质越优先起作用。当你调用带混入的类的方法是,最右侧特质的方法首先被调用。如果那个方法调用了 super,它调用其左侧特质的方法。可以这么认为,Doubling 的 super 指向了 Incrementing,Incrementing 的 super 指向了 BasicIntQueue。
来看个完整的实例实际体验一把,如果要帮助理解,最好应该实际运行一下这个实例
1import scala.collection.mutable.ArrayBuffer
2
3/**
4 * @author Unmi
5 */
6object TestClient extends App {
7 val queue1 = (new BasicIntQueue with Incrementing with Doubling)
8 queue1.put(2) //Doubling.put(2*2)->Incrementing.put(4+1)
9 println(queue1.get()) //result is 5
10
11 val queue2 = (new BasicIntQueue with Doubling with Incrementing)
12 queue2.put(2) //Incrementing.put(2+1)->Doubling.put(2*3)
13 println(queue2.get()) //result is 6
14}
15
16abstract class IntQueue {
17 def get(): Int
18 def put(x: Int)
19}
20
21class BasicIntQueue extends IntQueue {
22 private val buf = new ArrayBuffer[Int]
23 def get() = buf.remove(0)
24 def put(x: Int) { buf += x }
25}
26
27trait Incrementing extends IntQueue {
28 abstract override def put(x: Int) {
29 super.put(x + 1)
30 }
31}
32
33trait Doubling extends IntQueue {
34 abstract override def put(x: Int) {
35 super.put(2 * x)
36 }
37}实例中两次声明 queue1 和 queue2 是采用了不同的顺序混入 Incrementing 和 Doubling 两个特质,执行的效果是不一样,输出分别是 5 和 6。这种对 super 的重新理解会带来冲击,同时很有可能在代码维护时调整混入的特质时引来些许的麻烦。
混入特质存在着一种线性化的次序关系,再来看一下这个线性化的例子:
1class Animal
2trait Furry extends Animal
3trait HasLegs extends Animal
4trait FourLegged extends HasLegs
5class Cat extends Animal with Furry with FourLegged下图是 Cat 类的继承层级和线性化次序的展示图
继承次序使用白色三角箭头表示,箭头指向超类,黑底箭头说明线性化次序,箭头指向 super 调用解决的方向。
永久链接 https://yanbin.blog/scala-trait-super-dynamic-binding/, 来自 隔叶黄莺 Yanbin's Blog[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。