出现这种行为的原因是 Scala 的类线性化,它用于解决歧义和语义abstract override
。但首先要说的是。
类线性化
每当你有一个实例时a
类型的A
然后你调用它的方法a.foobar()
,编译器必须弄清楚在哪里可以找到定义foobar
. Since A
可以扩展任何其他类和一组特征,该函数可能有多个定义foobar
。为了解决这些歧义,Scala 将对你的类进行线性化A
及其所有超类和特征。线性化将产生一个顺序,在该顺序中检查不同类型的定义foobar
。第一个匹配将是执行的函数。
Scala 规范将线性化定义如下
定义 5.1.2 设 C 是一个具有模板 C1 且 ... 具有 Cn { stats } 的类。
C、L(C)的线性化定义如下:
L(C) = C , L(Cn)+: ... +: L(C1)
这里 +: 表示串联,其中右操作数的元素替换左操作数的相同元素。
由于所有理论都是灰色的,让我们看一个例子:
trait T1 {
def foobar() = 1
}
trait T2 {
def foobar() = 2
}
class B extends T2 {
override def foobar() = 42
}
class A extends B with T1 with T2 {
override def foobar() = super.foobar()
}
首先,我们必须重写foobar
类中的方法A
,因为我们对它有多个相互竞争的定义。然而,现在的问题是,调用了哪个方法定义super.foobar
。为了找到这一点,我们必须计算线性化A
.
L(A) = A, L(T2) +: L(T1) +: L(B)
L(B) = B, L(T2)
L(T2) = T2
L(T1) = T1
L(A) = A, T2 +: (T1, B, T2)
L(A) = A, T1, B, T2
Thus, super.foobar
将调用定义T1
返回1
.
抽象覆盖
The abstract override
方法的修饰符基本上表示必须有一个类/特征I
实现此方法,该方法出现在特征之后abstract override
实例化类的类线性化中的修饰符。这是执行所必需的super.foobar()
, 因为super.foobar()
需要进一步寻找线性化的定义foobar
.
当你现在看看类的定义时C
然后你会看到它具有以下线性化
C, B, A
因此,它无法编译,因为从B
你没有找到一个实现test
.
当我们现在查看有效的示例时,我们就会明白为什么它们实际上有效。如果是C extends A
with new C with B
,你基本上创建了一个匿名类Z extends C with B
。的线性化Z
is
Z, B, C, A
在那里你看到了,那B
可以找到C
的实施test
。这样,代码就可以编译了。对于类的例子也是如此D
.