定义协变和逆变类型时出错

时间:2013-01-11 12:27:13

标签: scala

我有代码:

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出现在T型的逆变位置   价值x           def set(x:T){current = x}

请解释

2 个答案:

答案 0 :(得分:5)

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

  • make T invariant
  • 修改set方法,使其返回CellCell的新实例,从而变为不可变。
  • 删除set方法,同时使Cell不可变

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

val cs:Cell[String] = new Cell("")
val ca:Cell[Any] = cs
ca.set(5)
val s:String = cs.get //boom

答案 1 :(得分:4)

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

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

第一行是允许的,因为Cell[B] Cell[A],因为使用了+T。然后第二行也被允许 - 因为您当然可以设置Cell[A]来保留A

但现在,cB.get会返回A的实例,而不是B!这没有任何意义,并且在仍然允许Cell的类型参数中的协方差的同时无法解决这个矛盾。


要获得更长,更正式/更全面的答案,请参阅此问题:Scala covariance/contravariance