我有代码:
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}
请解释
答案 0 :(得分:5)
类型的逆变位置是(以及其他)允许您将该类型的实例传递给方法的任何位置。因此所有方法参数类型都处于逆变位置。由于您将T声明为协变(+T
),因此编译器不允许这样做。您唯一的选择是:
T
invariant Cell
和Cell
的新实例,从而变为不可变。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