可堆叠特征模式:方法的实现“需要‘抽象覆盖’修饰符"; [英] Stackable Traits Pattern : method's implementation "needs `abstract override' modifiers"

查看:37
本文介绍了可堆叠特征模式:方法的实现“需要‘抽象覆盖’修饰符";的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,我了解了可堆叠特征模式,并遵循了此处所述的示例.一切正常,但有一种情况我无法理解:

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 将调用返回 1T1 中的定义.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆