最近,我发现了关于可堆叠特征模式的信息,并按照here描述的示例进行了学习。一切正常,但有一种情况我无法理解:
trait A {
def test : String
}
trait B extends A {
// 'abstract override' modifier required as
// the test() method is not yet implemented
abstract override def test = {
s"B${super.test}"
}
}
class C extends A with B {
// test method concrete implementation
override def test = { "C" }
}
<console>:10: error: overriding method test in trait B of type => String;
method test needs `abstract override' modifiers
class C extends A with B { override def test = { "C" } }
我不明白为什么它不能编译,以及为什么C::test方法需要提到的修饰符。
我注意到为了进行此编译,我可以做两个修改,可以通过在运行时编写C类来完成:
class C extends A { override def test = { "C" } }
new C with B // works as expected
或通过添加一个额外的类(有点相同,但在编译时):
class C extends A {
override def test = { "C" }
}
class D extends C with B
new D().test
res5: String = BC
为什么我需要一个额外的类(哪个BTW扮演Basic class的角色)?
最佳答案
出现这种情况的原因是Scala的类线性化,用于解决歧义和abstract override
的语义。但是首先是第一件事。
类线性化
每当您具有类型a
的实例A
并在其上调用a.foobar()
的方法时,编译器都必须弄清楚在哪里可以找到foobar
的定义。由于A
可以扩展任何其他类和一组特征,因此foobar
函数可能有多个定义。为了解决这些歧义,Scala将使用其所有父类(super class)和特征线性化您的类A
。线性化将产生一个顺序,在该顺序中检查不同类型的foobar
的定义。第一个匹配项将是执行的功能。
Scala规范定义了线性化,如下所示
Definition 5.1.2 Let C be a class with template C1 with ... with Cn { stats }. The linearization of C, L(C) is defined as follows: L(C) = C , L(Cn)+: ... +: L(C1)
Here +: denotes concatenation where elements of the right operand replace identical elements of the left operand.
由于所有理论都是灰色的,因此让我们看一个例子:
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
因此,
super.foobar
将调用T1
中的定义,该定义返回1
。抽象覆盖
方法的
abstract override
修饰符基本上说必须有一个实现此方法的类/特征I
,它在实例化的类的线性化中以abstract override
修饰符出现在特征之后。这是执行super.foobar()
所必需的,因为super.foobar()
要求进一步搜索线性化以获取foobar
的定义。现在,当您查看类
C
的定义时,您将看到它具有以下线性化 C, B, A
因此,它无法编译,因为从
B
开始,您找不到test
的实现。现在,当我们查看有效的示例时,我们将说明它们为何真正起作用。对于
C extends A
和new C with B
的情况,基本上是创建一个匿名类Z extends C with B
。 Z
的线性化为Z, B, C, A
在这里,您可以看到
B
可以在C
中找到test
的实现。因此,代码可以编译。类D
的示例也是如此。
关于scala - 可堆叠特征模式: method's implementation “needs ` abstract override' modifiers”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32511722/