2012-02-03 104 views
3

在我的應用我想用這樣的特點:斯卡拉特質及其方法參數化

trait HasBuffer[+A] { 

    var items = Buffer[A]() 

    def add[A](item: A) { items += item } 
    def remove[A](item: A) { items -= item } 
    def set(is: Buffer[A]) { items = is } 
    def clear { items clear } 
} 

類繼承這一特點應該能夠緩衝的誰是A類的子類的任何實例然而上添加和移除這兩種方法的編譯器抱怨被添加或刪除的項目從項「類型不匹配;實測值:項目。形式(與下面的A型)需要:A」。我應該如何理解這一點?我在這裏犯了什麼錯誤,該怎麼辦?

回答

5

您正在使用參數化從類定義的一個不同的另一種類型的參數A方法。這裏是你用改名參數寫的版本:

trait HasBuffer[+A] { 

    var items = Buffer[A]() 

    def add[B](item: B) = items += item 
    def remove[B](item: B) { items -= item } 
    def set(is: Buffer[A]) { items = is } 
    def clear { items clear } 
} 

現在應該清楚,爲什麼編譯器會拒絕這個。

相反,你可以簡單地寫這樣的方法:

def add(item: A) = items += item 
def remove(item: A) { items -= item } 

但是,你仍然會收到編譯器錯誤指出協變型A的禁忌和不變的位置出現。

協變的一點是,如果你期望HasBuffer[AnyVal]可以通過HasBuffer[Int]。但是,如果您希望AnyVal,並使用該類型的add方法爲好,你就可以到一個完全不同類型添加到您的HasBuffer[Int]。因此,編譯器拒絕這個。

相反,你必須提供一個下界類型參數是這樣的:

trait HasBuffer[+A, V <: A] { 

    var items = Buffer[V]() 

    def add(item: V) = items += item 
    def remove(item: V) { items -= item } 
    def set(is: Buffer[V]) { items = is } 
    def clear { items clear } 
} 

有了這個,你現在可以有一個像下面的方法:

def doSomething[X <: AnyVal](b : HasBuffer[AnyVal, X], e : X) = b.add(e) 

這種方法將工作在各種滿足所需下限的HasBuffer類型參數組合上。

精神上比較這與沒有提供一個下界的想法。然後,該方法會成爲這樣的事情:

// doesn't make much sense! 
def doSomething(b : HasBuffer[AnyVal], e : AnyVal) = b.add(e) 

如果調用此方法HasBuffer[Int]類型的對象和Double這將是所有快樂。你可能會不高興lateron不過,當你發現你的緩衝區應該只包含Int小號現在包含一個Double

+0

是的,我不得不承認這個解釋是非常詳細的,因此是一個可能遇到類似麻煩的人的方向。至於Scala編譯器 - 太糟糕了,它確實允許這種等價命名的類型參數。完全禁止它們是合乎邏輯的。由於沒有人願意將他的類型參數命名爲類似這樣的類型參數以及那些想要這樣做的類型參數,因此只會聲明不好的樣式編碼。代碼混淆是一個側面,它不應該出現在純編程中。 – noncom 2012-02-03 13:42:35

4

的問題是,你所定義的添加和移除相對於其他類型的參數A,雖然它具有相同的名稱,確實是一個新的類型參數的方法。

這應該讓你去:

def add(item: A) 
def remove(item: A) 

編輯:弗蘭克是正確的,我忘了處理的事實,BufferA協變如在原有聲明,A顯然是一個逆變位置。因此,以上只是解決OP問題的部分解決方案。

+0

一個非常好的答案,泰克! – noncom 2012-02-03 12:28:03

+0

-1。即使OP接受它,但這並不能解決協變類型在逆變位置中使用的問題。或者用更簡單的話來說:它不會隨着這種變化而編譯。 – Frank 2012-02-03 13:08:03