scala – Stackable Traits Pattern:方法的实现“需要`抽象覆盖
最近,我发现了可堆叠的特征模式,并按照
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的类线性化,它用于解决歧义和抽象覆盖的语义.但首先要做的事情.
类线性化 每当你有一个类型A的实例a并且在它上面调用一个方法a.foobar()时,编译器必须找出在哪里找到foobar的定义.由于A可以扩展任何其他类和一组特征,因此foobar函数可能有多个定义.为了解决这些歧义,Scala将使用其所有超类和特征线性化您的A类.线性化将产生一个顺序,在该顺序中检查不同类型的foobar定义.第一个匹配将是执行的功能. Scala规范将线性化定义如下
由于所有理论都是灰色的,让我们来看一个例子: 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的线性化. 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,T2 因此,super.foobar将在T1中调用返回1的定义. 抽象覆盖 方法的抽象覆盖修饰符基本上表示必须有一个类/特性我实现此方法,该特征在实例化类的类线性化中使用抽象覆盖修饰符出现在特征之后.这对于执行super.foobar()是必要的,因为super.foobar()需要进一步搜索线性化以寻找foobar的定义. 当您现在查看C类的定义时,您会发现它具有以下线性化 C,A 因此,它无法编译,因为从B开始你没有找到test的实现. 当我们现在看看有效的例子时,我们就会知道它们为什么会起作用.在C扩展A的情况下,使用带有B的新C,您基本上创建了一个匿名类Z扩展C和B. Z的线性化是 Z,C,A 在那里你看,B可以在C中找到测试的实现.因此,代码可以编译.对于D类的例子也是如此. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |