Scala的特质混合调用链

pro*_*eek 7 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 
}
Run Code Online (Sandbox Code Playgroud)

执行时,打印出来

M
Impl
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*don 11

是的,它被称为"线性化",它以一种聪明的方式解决了多重继承增强的钻石问题.

检查链接(或原始论文,你将学到更多我能给出的快速答案,但基本思路是:你从多个特征或抽象类继承的顺序很重要.Scala将创建一个单一的继承线,没有休息,通过按顺序挑选父母并调用最近的覆盖可用.

或者更好的是,请查看Scala编程第12章第12章中的规范示例:


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
Run Code Online (Sandbox Code Playgroud)

类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,特性Furry的线性化,但是现在已经在动物线性化中的所有类都被省略了,所以每个类在Cat的线性化中只出现一次.结果是:

image images/FurryLine.jpg

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

image images/FourLeggedLine.jpg

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

image images/CatLine.jpg

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