我有一个来自Java库的基类,我的代码无法修改。这个类(A)有一个空方法(b),它应该被声明为abstract:
class A {
def b { }
}
我在Scala中扩展此类并覆盖该方法以使其成为抽象:
abstract class AA extends A {
override def b
}
现在我在一个特性中实现这个方法:
trait B {
def b { println("B") }
}
如果我使用特征B扩展AA,我会收到一个错误:在类A =>的A类中重写方法b单元; 类型=>的特征B中的方法b单位需要`覆盖'修饰符:
class C extends AA with B {}
相反,如果代码是这样的,那么编译所有内容都没有错误,这对我来说似乎有些矛盾:
abstract class AA {
def b
}
trait B {
def b { println("B") }
}
class C extends AA with B {}
我正在运行Scala 2.8.0RC3,并且对该语言完全陌生(3天)。另一个奇怪的和相关的行为是在制作b abstract时不需要覆盖标签:
abstract class AA extends A {
def b
}
答案 0 :(得分:5)
为了试着看看发生了什么,我试了一下:
scala> class A{
| def b{ }
| }
defined class A
scala> abstract class AA extends A{
| override def b
| }
defined class AA
scala> class AAA extends AA{
| def b = println("AAA")
| }
<console>:8: error: overriding method b in class A of type => Unit;
method b needs `override' modifier
def b = println("AAA")
^
显然,问题的根源在于抽象类不能“释放”超类中的方法,而不需要抽象类的子类来包含“覆盖”修饰符。
答案 1 :(得分:2)
不确定这是否是正确的解决方案,但如果您的trait B
扩展A
(并覆盖b
),那么一切正常编译:
首先让我们定义A
和AA
,就像你在问题中提出它们一样:
C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class A {
| def b { println("A b") }
| }
defined class A
scala> new A
res5: A = A@153bedc4
scala> res5.b
A b
scala> abstract class AA extends A {
| override def b
| }
defined class AA
你做了什么:
scala> trait B {
| override def b { println("B b") }
| }
<console>:6: error: method b overrides nothing
override def b { println("B b") }
^
我尝试使用特质B(为了能够添加'override
'):
scala> trait B extends A {
| override def b { println("B b") }
| }
defined trait B
现在:
scala> class C extends AA with B {}
defined class C
scala> new C
res7: C = C@1497b7b1
scala> res7.b
B b
使用b
C.b
overriden方法
至于您明显的“不一致”,请参阅Scala for Java Refugees Part 5: Traits and Types:
首先,有一个令人烦恼的
override
关键字。我在关于基本OOP的文章中提到过,必须使用override修饰符声明覆盖超类中方法的任何方法。当时,我把它比作要求使用@Override
注释的语言,其主要目的是强制执行良好实践。特征能力的真正关键在于编译器在继承类中处理它们的方式 Traits实际上是mixins,而不是真正的父类 任何非抽象特征成员实际上都包含在继承类中,就像在类的物理部分中一样。好吧,不是在身体上,但是你得到了照片 就像编译器使用非抽象成员执行剪切和粘贴一样,并将它们插入到继承类中。这意味着继承路径中没有歧义,这意味着没有钻石问题。
因此,您的第二个示例中不需要override
关键字。
答案 2 :(得分:2)
问题非常微妙。根据经验,您的 AA 类(扩展 A )应该与也扩展 A 的特征混合使用。
你做了:
class A {
def b { }
}
abstract class AA extends A {
override def b
}
trait B {
def b { println("B") }
}
因此,当您混合 AA 和 B 方法时, b 将被定义两次。一旦通过 A (未被覆盖,因为 B 中的定义替换了 AA 中的覆盖)而第二个被 B 覆盖,编译器不能选择其中一个,因为两个(等于命名但不相关)的方法之间没有层次结构。如果你愿意,可以考虑这样:编译器“混合” AA 和 B 的主体;如果他从 AA 中选择方法,那么它将是抽象的,如果他从 B (应该发生什么)中选择方法,因为它不是覆盖,你坚持使用两种方法 b'/ em>的。
要解决此问题,您需要确保两个方法override
使用相同的方法,在这种情况下,编译器将理解您正在讨论SAME方法,并优先考虑混合的最后一个特征。 / p>
现在,要覆盖 B 中的 b 方法,该类还必须继承 A 。所以这样做的规范方法是:
class A {
def b { }
}
abstract class AA extends A {
override def b
}
trait B extends A{
def b { println("B") }
}
class C extends AA with B {}
哪个编译得很好。
现在,当你这样做时:
abstract class AA {
def b
}
trait B {
def b { println("B") }
}
class C extends AA with B {}
很明显,两种方法都是相同的,所以编译器知道他必须使用特征中的方法。
其他解决方案包括:
同样,问题非常微妙,但我希望我能让它更清晰一些。要获得更好的理解,请阅读Scala's Stackable Trait Pattern。