I have code:
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)
but I have error in line: def set(x: T) { current = x }
error: covariant type T occurs in contravariant position in type T of value x def set(x: T) { current = x }
Explain, please
Contravariant positions for a type are (among others) any positions that allow you to pass an instance of that type to a method. So all method parameter types are in contravariant positions. Since you declared T as covariant ( +T
), the compiler won't allow this. Your only options are:
T
invariantCell
and Cell
thus becomes immutable.Cell
immutableIf the compiler allowed you to have a set method as you implemented it, that would make the type system unsafe, since it would allow you to write:
val cs:Cell[String] = new Cell("")
val ca:Cell[Any] = cs
ca.set(5)
val s:String = cs.get //boom
The short answer is that you cannot have a mutable container which is covariant. Assuming this compiled, then in your example, the last two lines could read:
val cA: Call[A] = cB
cA.set(new A)
The first line would be allowed, because a Cell[B]
is a Cell[A]
due to the use of +T
. And then the second line is allowed too - because of course you can set a Cell[A]
to hold an A
.
But now, cB.get
returns an instance of A
, not a B
! This doesn't make any sense, and there is no way to resolve this contradiction while still allowing covariance in Cell's type parameter.
For a longer, more formal/comprehensive answer, see this question: Scala covariance/contravariance
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With