scala - 可堆叠特征模式: method's implementation “needs ` abstract override' modifiers”

标签 scala

最近,我发现了关于可堆叠特征模式的信息,并按照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 Anew C with B的情况,基本上是创建一个匿名类Z extends C with BZ的线性化为
Z, B, C, A

在这里,您可以看到B可以在C中找到test的实现。因此,代码可以编译。类D的示例也是如此。

关于scala - 可堆叠特征模式: method's implementation “needs ` abstract override' modifiers”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32511722/

相关文章:

scala - 在 Scala 中注释构造函数参数

json - 为什么 Scala 编译器会区别对待这两个代码块?

scala - SBT 1.0 崩溃 - java.lang.VerifyError : Uninitialized object exists on backward branch

java - Scala 在非常长的字符串上调用 length() 会引发异常 : Maximum String literal length exceeded

scala - 如何约束参数类的成员以匹配外部类的实例?

c# - 为什么LINQ操作会丢失集合的静态类型?

scala - 在 Play 框架中代理请求

scala - Apache Spark 项目的 "./sbt/sbt assembly"错误 "Not a valid command: assembly"

javascript - 在 Play-scala 框架中加载另一个 Javascript 中的 Javascript

json - Play Json API : Convert a JsArray to a JsResult[Seq[Element]]