2010-07-04 54 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

相關問題