The top answer of this question explains why method arguments are contravariant. The author says that if this would compile:
case class ListNode[+T](h: T, t: ListNode[T]) {
def head: T = h
def tail: ListNode[T] = t
def prepend(elem: T): ListNode[T] =
ListNode(elem, this)
}
Then this would be okay:
val list1: ListNode[String] = ListNode("abc", null)
val list2: ListNode[Any] = list1 // list2 and list1 are the same thing
val list3: ListNode[Int] = list2.prepend(1) // def prepend(elem: T): ListNode[T]
Note that I have removed the last row of the original snippet.
My thought process is the following:
- On line 1, a
ListNode[String]
calledlist1
is assigned a newListNode(someString, null)
. Nothing strange here.list1
is not aListNode[String]
. - On line 2, a
ListNode[Any]
gets assignedlist1
. This is fine becauseListNode
is covariant andAny
is a supertype ofString
. - On line 3, the
prepend
method oflist2
is called. Sincelist2
is aListNode[Any]
,list2.prepend()
should return aListNode[Any]
. But the result of the method call is assigned to aListNode[Int]
. This can not possibly compile becauseInt
is a subtype ofAny
andListNode
is NOT contravariant!
Have I misunderstood something? How can the author claim this would ever compile?