Scala的特质混合调用链

时间:2015-08-21 02:56:03

标签: scala traits

我有这个Scala代码:

trait Foo {
    def foo()
}

trait M extends Foo {
    abstract override def foo() {println("M"); super.foo()}
}

// interface implementation
class FooImpl1 extends Foo {
    override def foo() {println("Impl")}
}

class FooImpl2 extends FooImpl1 with M

object Main extends App {
    val a = new FooImpl2
    a.foo 
}

执行时,打印出

M
Impl

我很好奇的是特质方法背后的机制。在这种情况下,首先调用特征M中的foo,然后super.foo()调用FooImpl1.foo()的具体调用。这个调用链背后的逻辑是什么?有关于此行为的任何文档吗?

1 个答案:

答案 0 :(得分:10)

是的,它被称为"linearization",它以一种聪明的方式解决了讨厌的Diamond Problem of Multiple Inherithance

检查链接(或original paper,你会学到更多我能给出的快速答案,但基本思路是:你从多个特征或抽象类继承的顺序很重要。 Scala将通过按顺序选择父级并调用最接近的覆盖来创建单个继承行,没有中断。

或者更好的是,请查看Chapter 12 of Programming in Scala, First Edition中的规范示例:

Scala线性化的主要属性通过以下示例说明:假设您有一个类Cat,它继承自超类Animal和两个特征Furry和FourLegged。 FourLegged继续延伸另一个特征HasLegs:

 class Animal 
  trait Furry extends Animal
  trait HasLegs extends Animal
  trait FourLegged extends HasLegs
  class Cat extends Animal with Furry with FourLegged

类Cat的继承层次结构和线性化如图12.1所示。使用传统的UML表示法表示继承:带有白色三角形箭头的3箭头表示继承,箭头指向超类型。带有黑色非三角形箭头的箭头描绘了线性化。黑暗的箭头指向超级呼叫将被解决的方向。

image images/linearization.jpg

图12.1 - 类Cat。

的继承层次结构和线性化

Cat的线性化从后到前计算如下。 Cat线性化的最后一部分是其超类Animal的线性化。无需任何更改即可复制此线性化。 (这里表12.1中显示了每种类型的线性化。)因为Animal没有显式扩展超类或混合任何超类,所以它默认扩展AnyRef,它扩展了Any。因此,Animal的线性化看起来像:

image images/AnimalLine.jpg

倒数第二部分是第一个mixin,trait Furry的线性化,但是现在已经排除了已经在Animal线性化中的所有类,因此每个类在Cat的线性化中只出现一次。结果是:

image images/FurryLine.jpg

这之前是FourLegged的线性化,其中任何已经在超类或第一个mixin的线性化中复制的类都被省略了:

image images/FourLeggedLine.jpg

最后,Cat线性化的第一个类是Cat本身:

image images/CatLine.jpg

当这些类和特性中的任何一个通过super调用方法时,调用的实现将是线性化中右边的第一个实现。

相关问题