2011-01-08 7 views
1

AとBの2つのクラスがあり、BのサブタイプがAであるとします。これはより豊富な型階層の一部ですが、それは関係ないと思います。 Aが階層のルートであると仮定する。 Aのリストを追跡するコレクションクラスCがあります。しかし、私は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) 
    } 
} 

は明らかエラー与える上記コード:new B(c)行に '型の不一致は、Cは、[B]、C [A]を必要見出さ'。

これを修正する方法がわかりません。 ArrayBufferはTに型が変わっていないので、TでC共変を作ることはできません(BのコンストラクタにC [B]が必要です).Cは共変できないためです。

ここで間違った木を吠えていますか?私はScala初心者ですから、どんなアイデアやヒントも参考になるかもしれません。ありがとうございました!

EDIT: 基本的に、私がしたいのですが、コンパイラは

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の型をTの代わりにAにするのを緩和するために、 addEntryメソッドも役立ちます。

答えて

1

ArrayBufferが非variantly T

このための

ないだけで入力されるので、それはT(のようなC[+T <: A])でCの共変を作成することはできません。

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

val cb: C[B] = ... 

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

ハックが、動作しているようです:addEntryの種類は、それを拒否するのに十分です。もちろん、

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のインスタンスを追跡したい場合、BのコンストラクタにC [A]のインスタンスを渡す必要があります。すべてのBもAであるからです。

しかし、Bを追跡したい場合、AはBについて何も知らないので、これをAに委譲することはできません。

全体的に、私はあなたの問題が幾分悪いと感じています。

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) 
を拒否する210

しかし、BC[B]で初期化され、場合B、それによって最後の要件に違反し、C[B]Aを初期化し、その後BA延びていることを考えます。

+0

Cが共変型である場合、ここで説明する理由は間違いですが、厳密には要件ではありません。 C [B]をC [A]のサブタイプにする必要はないと思います。 – Verhoevenv

+0

@Verhoevenvもし 'C [B]'が 'C [A]'のサブタイプでなければ、 'C [B]'型の 'c'が' '新しいB(c) ?私はあなたのリビジョンを今見ていて、あなたは何か不可能なことを求めています。 –