2011-01-08 71 views
1

假設我有兩個A和B類,B是A的一個子類型。顯然,這只是更豐富類型層次結構的一部分,但我認爲這不是相關的。假設A是層次結構的根。有一個集合類C跟蹤A的列表。但是,我想使C通用,這樣就有可能創建一個只保留B而不接受A的實例。雙向關聯中的泛型

class A(val c: C[A]) { 
    c.addEntry(this) 
} 
class B(c: C[A]) extends A(c) 
class C[T <: A]{ 
    val entries = new ArrayBuffer[T]() 
    def addEntry(e: T) { entries += e } 
} 
object Generic { 
    def main(args : Array[String]) { 
     val c = new C[B]() 
     new B(c) 
    } 
} 

以上明顯得到錯誤代碼 '類型不匹配:實測值C [B],需要C [A]' 的new B(c)線。

我不知道如何解決這個問題。在T中不可能產生C協變(如C[+T <: A]),因爲ArrayBuffer在T中是非變化類型的。不可能使B的構造函數需要C [B],因爲C不能是協變的。

我在這兒吠錯了樹嗎?我是一個完整的Scala新手,所以任何想法和提示可能會有所幫助。謝謝!

編輯: 基本上,我想有是編譯器可同時接收

val c = new C[B]() 
new B(c) 

val c = new C[A]() 
new B(c) 

,但會拒絕

val c = new C[B]() 
new A(c) 

這也可能是可能的放寬在C中的ArrayBuffer的鍵入爲A而不是T,從而在C中addEntry方法,如果有幫助的話。

回答

1

它不可能使C協在T(像C[+T <: A]),因爲ArrayBuffer非variantly T中

,不僅是因爲這類型的。該類型的addEntry足以禁止它:

val a: A = ... 
val b: B = ... 

val cb: C[B] = ... 

cb.addEntry(b) // works 
cb.addEntry(a) // doesn't and shouldn't 
0

哈克,但似乎工作:

class A(val c: C[A]) { 
    c.addEntry(this.asInstanceOf[c.X]) 
} 

class B(c: C[B]) extends A(c) 

class C[+T <: A] { 
    type X <: T 
    val entries = new ArrayBuffer[X]() 
    def addEntry(e: X) { entries += e } 
} 

object Generic { 
    def main(args : Array[String]) { 
     val c = new C(){ type T = B } 
     new B(c) 
    } 
} 

我當然有興趣在一個適當的解決方案,以及...

0

如果要跟蹤A的實例,則必須將C [A]的實例傳遞給B的構造函數,因爲每個B也都是A:

def main(args : Array[String]) { 
    val c = new C[A]() 
    new B(c) 
} 

但是如果你想跟蹤Bs,那麼你不能把它委託給A,因爲A不知道關於B的任何內容。

總的來說,我感覺你的問題有點不適合。

0

讓我們說這是可能的。那麼你可以這樣做:

class A(val c: C[A]) { 
    c.addEntry(this) 
} 
class B(c: C[A]) extends A(c) 
class C[+T <: A]{ 
    val entries: ArrayBuffer[T] @uncheckedVariance = new ArrayBuffer[T]() 
    def addEntry(e: T @uncheckedVariance) { entries += e } 
} 
object Generic { 
    def main(args : Array[String]) { 
     // Everything's fine so far... 
     val c = new C[B]() 
     c.addEntry(new B(c)) 
     // but, suddenly... 
     val ca: C[A] = c 
     ca.addEntry(new A(ca)) 
     // a problem appears! 
     c.entries forall { 
      case thing: B => true // ok 
      case otherThing => false // not ok -- c now contains an A! 
     } 
    } 
} 

試圖運行此代碼將導致類拋出異常。

編輯

您加入這個要求:

val c = new C[B]() 
new B(c) 

val c = new C[A]() 
new B(c) 

,但會拒絕

val c = new C[B]() 
new A(c) 

然而,如果BC[B]初始化,並考慮到B延伸A,然後B將初始化AC[B],從而違反了最後一個要求。

+0

如果C是協變的,它確實出現了你在這裏描述的原因,但這不是嚴格的要求。我不認爲有必要使C [B]成爲C [A]的一個子類型。 – Verhoevenv 2011-01-10 17:57:42