2011-01-08 8 views
1

Angenommen, ich habe zwei Klassen A und B, mit B ein Subtyp von A. Dies ist offensichtlich nur Teil einer reicheren Typhierarchie, aber ich denke nicht, dass das relevant ist. Angenommen, A ist der Stamm der Hierarchie. Es gibt eine Erfassungsklasse C, die eine Liste von As verfolgt. Allerdings möchte ich C generisch machen, so dass es möglich ist, eine Instanz zu erstellen, die nur Bs behält und keine A akzeptiert.Generics in einer bidirektionalen Assoziation

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

Der obige Code offensichtlich den Fehler geben 'Typ Mismatch: gefunden C [B] erforderlich C [A]' auf der Linie new B(c).

Ich bin mir nicht sicher, wie das behoben werden kann. Es ist nicht möglich, C in T kovariant zu machen (wie C[+T <: A]), weil der ArrayBuffer in T nichtvariabel typisiert ist. Es ist nicht möglich, den Konstruktor von B ein C [B] zu verlangen, weil C nicht kovariant sein kann.

Banne ich hier den falschen Baum? Ich bin ein kompletter Scala-Neuling, also können Ideen und Tipps hilfreich sein. Vielen Dank!

EDIT: Im Grunde, was ich möchte ist haben, dass der Compiler akzeptiert sowohl

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

und

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

aber ablehnen würden

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

Es ist wahrscheinlich möglich um die Typisierung des ArrayBuffers in C zu entspannen, um A statt T zu sein, und somit in der addEntry-Methode auch, wenn das hilft.

Antwort

1

Es ist nicht möglich, C covariant in T (wie C[+T <: A]), da die Arraybuffer ist nicht variantly getippt in T

Nicht nur, weil dieser zu machen. Die Art der addEntry ist genug, um es nicht zuzulassen:

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

val cb: C[B] = ... 

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

Hacky, aber scheint zu funktionieren:

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

Natürlich habe ich auch in einer geeigneten Lösung interessiert wäre ...

0

Wenn Sie die Instanzen von A verfolgen wollen, müssen Sie eine Instanz von C [A] an den Konstruktor von B übergeben, da jedes B ebenfalls ein A:

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

Wenn Sie jedoch die Bs verfolgen möchten, können Sie dies nicht an A delegieren, da A nichts über B weiß.

Insgesamt habe ich das Gefühl, dass Ihr Problem etwas schlecht gestellt ist.

0

Sagen wir es war möglich. Dann könnten Sie Folgendes tun:

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

Der Versuch, diesen Code auszuführen, führt zu einer Klassenausnahme.

bearbeiten

Sie haben diese Anforderung:

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

und

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

aber

val c = new C[B]() 
new A(c) 
ablehnen würde 210

Wenn jedoch B mit C[B] initialisiert, und da BA erstreckt, dann B wird A mit C[B] initialisieren, wodurch die letzte Anforderung zu verletzen.

+0

Es ist in der Tat falsch für genau die Gründe, die Sie hier beschreiben, wenn C kovariant ist, aber das ist nicht unbedingt eine Voraussetzung. Ich glaube nicht, dass es notwendig ist, C [B] zu einem Subtyp von C [A] zu machen. – Verhoevenv

+0

@Verhoevenv Wenn 'C [B]' kein Untertyp von 'C [A]' war, wie können Sie erwarten, dass 'new B (c)' funktioniert, wenn 'c' vom Typ' C [B] 'ist ? Ich sehe gerade nach Ihrer Revision und Sie verlangen etwas Unmögliches. –

Verwandte Themen