可堆叠特征模式:方法的实现“需要‘抽象覆盖’修饰符"; [英] Stackable Traits Pattern : method's implementation "needs `abstract override' modifiers"
问题描述
最近,我了解了可堆叠特征模式,并遵循了此处所述的示例.一切正常,但有一种情况我无法理解:
Recently, I've find out about the stackable trait pattern and followed the example described here. Everything works, but there is a case I cannot understand :
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 方法需要提到的修饰符.
I cannot understand why this does not compile, and why the C::test method needs the mentioned modifier.
我注意到我可以通过在运行时组合 C 类来进行两个修改以进行编译:
I've noticed that there is two modifications I can do in order to make this compile, either by composing the C class at runtime :
class C extends A { override def test = { "C" } }
new C with B // works as expected
或者添加一个额外的类(这在编译时是一样的):
or by adding an extra class (which is kind of the same but at compile time):
class C extends A {
override def test = { "C" }
}
class D extends C with B
new D().test
res5: String = BC
为什么我需要一个额外的类(顺便说一下,它扮演了基础类的角色)?
Why do I need an extra class (which BTW plays the role of the Basic class) ?
推荐答案
这种行为的原因是 Scala 的类线性化,它用于解决抽象覆盖
的歧义和语义.但首先要做的是.
The reason for this behaviour is Scala's class linearization which is used to resolve ambiguities and the semantics of abstract override
. But first things first.
每当你有一个 A
类型的实例 a
并且你在它上面调用一个方法 a.foobar()
,编译器必须找出在哪里可以找到 foobar
的定义.由于 A
可以扩展任何其他类和一组特征,因此函数 foobar
可能有多个定义.为了解决这些歧义,Scala 会将您的类 A
及其所有超类和特征线性化.线性化将产生一个顺序,在该顺序中检查不同类型的 foobar
定义.第一个匹配将是执行的函数.
Whenever you have an instance a
of type A
and you call a method on it a.foobar()
, the compiler has to figure out where to find the definition of foobar
. Since A
can extend any other class and a set of traits, there might be multiple definitions for the function foobar
. In order to resolve these ambiguities, Scala will linearize your class A
with all its superclasses and traits. The linearization will produce an order in which the different types are checked for a definition of foobar
. The first match will be the function which is executed.
Scala 规范将线性化定义如下
The Scala specification defines the linearization as following
定义 5.1.2 令 C 为模板 C1 的类,其中 ... with Cn { stats }.C, L(C) 的线性化定义如下:L(C) = C , L(Cn)+: ... +: L(C1)
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.
既然所有的理论都是灰色的,我们来看一个例子:
Since all theory is grey, let's take a look at an example:
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()
}
首先,我们必须覆盖 A
类中的 foobar
方法,因为我们有多个相互竞争的定义.然而,现在的问题是,super.foobar
调用了哪个方法定义.为了找到这一点,我们必须计算 A
的线性化.
First of all, we have to override the foobar
method in the class A
, because we have multiple competing definitions for it. However, now is the question, which method definition is called by super.foobar
. In order to find this out, we have to calculate the linearization of 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
将调用返回 1
的 T1
中的定义.
Thus, super.foobar
will call the definition in T1
which returns 1
.
方法的 abstract override
修饰符基本上是说必须有一个类/trait I
实现这个方法,它出现在带有 abstract 的 trait 之后实例化类的类线性化中的 override
修饰符.这是执行 super.foobar()
所必需的,因为 super.foobar()
需要进一步搜索线性化以寻找 foobar的定义代码>.
The abstract override
modifier for a method basically says that there has to be a class/trait I
implementing this method which appears after the trait with the abstract override
modifier in the class linearization of your instantiated class. That is necessary in order to execute super.foobar()
, because super.foobar()
entails that the linearization is further searched for a definition of foobar
.
当您现在查看 C
类的定义时,您将看到它具有以下线性化
When you now look at your definition of class C
then you'll see that it has the following linearization
C, B, A
因此,它无法编译,因为从 B
开始,您找不到 test
的实现.
Consequently, it cannot compile, because beginning from B
you don't find an implementation of test
.
当我们现在查看有效的示例时,我们将了解它们实际有效的原因.在C extends A
with new C with B
的情况下,你基本上创建了一个匿名类Z extends C with B
.Z
的线性化为
When we now look at the examples which work, then we'll why they actually work. In the case of C extends A
with new C with B
, you basically create an anonymous class Z extends C with B
. The linearization of Z
is
Z, B, C, A
你看,B
可以在 C
中找到 test
的实现.这样,代码就可以编译了.对于带有 D
类的示例也是如此.
There you see, that B
can find in C
an implementation of test
. Thus, the code can compile. The same holds true for the example with class D
.
这篇关于可堆叠特征模式:方法的实现“需要‘抽象覆盖’修饰符";的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!