双向关联中的泛型

时间:2011-01-08 04:39:57

标签: generics inheritance scala bidirectional

假设我有两个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],new B(c)行需要C [A]'。

我不确定如何解决这个问题。在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,因此在addEntry方法中也是如此,如果有帮助的话。

4 个答案:

答案 0 :(得分:1)

  

在T中不能使C协变(如C[+T <: A]),因为ArrayBuffer在T

中是非变量类型的

不仅仅是因为这个原因。 addEntry的类型足以让它不允许:

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

val cb: C[B] = ...

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

答案 1 :(得分:0)

Hacky,但似乎有效:

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

当然,我也会对一个合适的解决方案感兴趣...

答案 2 :(得分:0)

如果你想跟踪A的实例,你必须将C [A]的实例传递给B的构造函数,因为每个B也是A:

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

如果你想跟踪Bs,那么你不能将其委托给A,因为A对B不了解。

总的来说,我觉得你的问题有点不对劲。

答案 3 :(得分: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)

但是,如果使用B初始化C[B],并且B扩展A,那么 B 将初始化A C[B],因此违反了最后一项要求。