2011-01-08 50 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