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

上面的代码显然给出了错误 'type mismatch: found C[B], required C[A]' 就new B(c)行了。

我不确定如何解决这个问题。不可能使 C 在 T 中协变(如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

4 回答 4

1

不可能使 C 在 T 中协变(如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
于 2011-01-08T05:07:54.327 回答
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)
    }
}

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

于 2011-01-08T14:00:32.827 回答
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],并且给定Bextends A,那么BA用初始化C[B],从而违反最后一个要求。

于 2011-01-10T13:16:07.737 回答
0

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

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

但是,如果您想跟踪 B,那么您不能将其委托给 A,因为 A 对 B 一无所知。

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

于 2011-01-08T17:23:23.993 回答