5

我有代码:

class A {
    override def toString = "object class A"
}

class B extends A {
    override def toString = "object class B"
}

class Cell[+T](init: T) {
    private[this] var current: T = init
    def get: T = current
    def set(x: T) { current = x }
}

val cB = new Cell[B](new B)
println(cB.get)
val cA: Cell[A] = cB
println(cA.get)

但我有错误:def set(x: T) { current = x }

错误:协变类型 T 出现在值 x def set(x: T) { current = x } 的类型 T 的逆变位置

请解释

4

2 回答 2

5

类型的逆变位置是(除其他外)允许您将该类型的实例传递给方法的任何位置。所以所有方法参数类型都处于逆变位置。由于您将 T 声明为协变 ( +T),因此编译器将不允许这样做。您唯一的选择是:

  • 使T不变
  • 修改 set 方法,使其返回一个新的实例,Cell从而Cell变得不可变。
  • 删除 set 方法,也使Cell不可变

如果编译器允许您在实现时使用 set 方法,那将使类型系统不安全,因为它允许您编写:

val cs:Cell[String] = new Cell("")
val ca:Cell[Any] = cs
ca.set(5)
val s:String = cs.get //boom
于 2013-01-11T12:32:42.170 回答
4

简短的回答是你不能有一个协变的可变容器。假设这是编译的,那么在您的示例中,最后两行可以读取:

val cA: Call[A] = cB
cA.set(new A)

第一行是允许的,因为 a是Cell[B] 由于Cell[A]使用+T. 然后第二行也被允许 - 因为当然你可以设置 aCell[A]来保存一个A.

但是现在,cB.get返回一个实例A,而不是一个B!这没有任何意义,也没有办法解决这个矛盾,同时仍然允许 Cell 的类型参数存在协变。


如需更长、更正式/更全面的答案,请参阅以下问题:Scala covariance/contravariance

于 2013-01-11T12:35:09.237 回答