2010-07-04 46 views
6

我有一个来自Java库的基类,其代码无法修改。这个类(A)有一个空的方法(b)应已被定义为抽象的,而不是:实现具有特征的不一致编译器行为的抽象方法?

class A { 
    def b { } 
} 

我在斯卡拉这个类扩展和覆盖的方法,使其摘要:

abstract class AA extends A { 
    override def b 
} 

现在我实现的性状此方法:

trait B { 
    def b { println("B") } 
} 

如果我延伸与性状b AA,得到了一个错误:在类型=>单位的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 class AA extends A { 
    def b 
} 
+0

这与你想通过混合你自己的'def toString():String'实现来代替Java类的Java toString实现是一样的吗? – huynhjl 2010-07-04 15:24:50

+0

不,这不是一回事。在这种情况下,编译器的行为总是有意义的,因为它不明确应该使用哪个定义:Java toString或者mixin。 在我的情况下,只有一个可用,但。 – 2010-07-04 21:48:38

回答

2

不知道这是正确的解决方案,但如果你的trait B延伸A(和覆盖b),那么一切编译罚款:

首先让我们来定义AAA就像你在你的问题呈现出来:

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 = [email protected] 

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 = [email protected] 

scala> res7.b 
B b 

b覆盖方法被调用与C.b


至于你明显“不一致”,见Scala for Java Refugees Part 5: Traits and Types

To start with, there’s that ever-annoying override keyword. I mentioned back in the article on basic OOP that any method which overrides a method in a superclass must be declared with the override modifier. At the time, I likened it to the language mandating the use of the @Override annotation, with its primary purpose being to enforce the good practice.

The real key to the power of traits is the way in which the compiler treats them in an inheriting class.
Traits are actually mixins, not true parent classes.
Any non-abstract trait members are actually included in the inheriting class, as in physically part of the class. Well, not physically, but you get the picture.
It’s as if the compiler performs a cut-and-paste with the non-abstract members and inserts them into the inheriting class. This means that there’s no ambiguity in the inheritance path, meaning no diamond problem.

所以没必要在你的第二个例子override关键字。

+0

感谢您的详细回复。我认为我了解特质,因为我来自Ruby,它有模块,类似的东西。 恕我直言,使特质B扩展A击败特质的目的,因为我不能在另一个类层次结构中使用这个特质,这正是我想要做的。有任何想法吗? 我认为问题在于Scala编译器,它没有看到方法b变得抽象,并且在叶类C中的特征中混合时,其定义中没有含糊不清的问题。 – 2010-07-04 12:01:11

+0

@ A.R:理解;我的答案受到了http://blog.objectmentor.com/articles/2008/09/29/a-scala-style-_with_-construct-for-ruby的启发。仍在检查其他解决方案。 – VonC 2010-07-04 12:16:10

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") 
      ^

显然,问题的根源是抽象类不能从需求的“释放”在其超类方法抽象类的子类包含'override'修饰符。

+0

那么,重写的东西可以通过在C类中取消def b = super [B] .b来取消编译器的歧义性来解决,但这不是必须的。这是我的观点。谢谢! – 2010-07-04 13:09:05

+0

MJP是正确的:具体成员永远不能在子类中被抽象化。子类中的抽象成员声明不起作用。这是mixin组合规则的结果,无论包含定义的特征的顺序是什么,混合总是优先于抽象。 – 2010-07-05 12:00:20

2

问题很微妙。作为一个经验法则,您的类AA,其中延伸A,应该混合性状,也扩展A

你做到:

class A { 
    def b { } 
} 

abstract class AA extends A { 
    override def b 
} 

trait B { 
    def b { println("B") } 
} 

因此,当你混合AA方法b被定义了两次。一旦通过(不overrided因为在定义在AA替换超驰)和第二通过,编译器不能选择一个在另一个之上,因为在二者之间没有层级(等同命名但不相关)的方法。如果你愿意,可以这样想:编译器“混合”AAB;如果他选择的方法从AA它将是抽象的,如果他从B(应该发生的事情)选择方法,因为它不是一个覆盖,你坚持两种方法b

为了解决这个问题,你要确保两个方法override都是相同的方法,在这种情况下,编译器会理解你正在谈论SAME方法,并会优先考虑混合的最后一个特征。

现在,要覆盖的方法b,该类也必须从继承。所以规范的做法是:

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 {} 

很显然,这两种方法都是相同的,所以编译器知道他从特征使用的方法。

其他解决方案包括:

  1. 覆盖AA
  2. b一个抽象的(但你不希望出现这种情况)

再次,问题非常微妙,但我希望我更清楚一点。为了得到更好的理解,请阅读Scala's Stackable Trait Pattern

+0

谢谢,你的解释非常清楚。 – 2010-07-07 09:51:03