我有这个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个箭头表示继承,箭头指向超类型.带有黑色非三角形箭头的箭头描绘了线性化.黑暗的箭头指向超级呼叫将被解决的方向.
图12.1 - 类Cat的继承层次结构和线性化.
Cat的线性化从后到前计算如下.Cat线性化的最后一部分是其超类Animal的线性化.无需任何更改即可复制此线性化.(这里表12.1中显示了每种类型的线性化.)因为Animal没有显式扩展超类或混合任何超类,所以它默认扩展AnyRef,它扩展了Any.因此,Animal的线性化看起来像:
倒数第二部分是第一个mixin,特性Furry的线性化,但是现在已经在动物线性化中的所有类都被省略了,所以每个类在Cat的线性化中只出现一次.结果是:
这之前是FourLegged的线性化,其中任何已经在超类或第一个mixin的线性化中复制的类都被省略了:
最后,Cat线性化的第一类是Cat本身:
当这些类和特性中的任何一个通过super调用方法时,调用的实现将是线性化中右边的第一个实现.