为什么输入参数在方法中是逆变的?
Why are input parameters contravariant in methods?
这是 this 教程中的一些代码:
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)
}
教程说:
Unfortunately, this program does not compile, because a covariance
annotation is only possible if the type variable is used only in
covariant positions. Since type variable T appears as a parameter type
of method prepend, this rule is broken.
为什么 T
不在 predend
中的协变位置,而其他 T
引用(def head: T = h
和 def tail: ListNode[T] = t
)显然是协变?
我要问的是为什么 prepend
中的 T
不是协变的。 Why is Function[-A1,...,+B] not about allowing any supertypes as parameters?当然没有涉及,这似乎是其他人指导我阅读的内容。
一个例子:
val dogList = ListNode[Dog](...)
val animal = Animal()
val dog = Dog()
dogList.prepend(dog) //works
dogList.prepend(animal) //fails
协方差意味着您可以像使用 ListNode[Animal] 一样使用 ListNode[Dog]
但是:
//if compiler would allow it
val animalList: ListNode[Animal] = dogList
animalList.prepend(dog) //works
animalList.prepend(animal) //would fail, but you expect an animallist to accept any animal
方法的输入参数不是协变位置而是逆变位置。只有 return 类型的方法处于协变位置。
如果你对 ListNode
class 的定义是 OK,那么我可以这样写代码:
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]
val x: Int = list3.tail.head // ERROR, x in fact is "abc"
看,如果参数 是 协变位置,那么一个容器总是可以保存另一个类型的值,该类型与其真实类型具有相同的祖先,这绝对是 错误!
所以,参考scala.collection.immutable.List.::的源代码,你的class应该是这样定义的:
case class ListNode[+T](h: T, t: ListNode[T]) {
def head: T = h
def tail: ListNode[T] = t
def prepend[A >: T](elem: A): ListNode[A] = ListNode(elem, this)
}
参数类型 A
是一个新的类型参数,其下界为 T
。
这是 this 教程中的一些代码:
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)
}
教程说:
Unfortunately, this program does not compile, because a covariance annotation is only possible if the type variable is used only in covariant positions. Since type variable T appears as a parameter type of method prepend, this rule is broken.
为什么 T
不在 predend
中的协变位置,而其他 T
引用(def head: T = h
和 def tail: ListNode[T] = t
)显然是协变?
我要问的是为什么 prepend
中的 T
不是协变的。 Why is Function[-A1,...,+B] not about allowing any supertypes as parameters?当然没有涉及,这似乎是其他人指导我阅读的内容。
一个例子:
val dogList = ListNode[Dog](...)
val animal = Animal()
val dog = Dog()
dogList.prepend(dog) //works
dogList.prepend(animal) //fails
协方差意味着您可以像使用 ListNode[Animal] 一样使用 ListNode[Dog]
但是:
//if compiler would allow it
val animalList: ListNode[Animal] = dogList
animalList.prepend(dog) //works
animalList.prepend(animal) //would fail, but you expect an animallist to accept any animal
方法的输入参数不是协变位置而是逆变位置。只有 return 类型的方法处于协变位置。
如果你对 ListNode
class 的定义是 OK,那么我可以这样写代码:
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]
val x: Int = list3.tail.head // ERROR, x in fact is "abc"
看,如果参数 是 协变位置,那么一个容器总是可以保存另一个类型的值,该类型与其真实类型具有相同的祖先,这绝对是 错误!
所以,参考scala.collection.immutable.List.::的源代码,你的class应该是这样定义的:
case class ListNode[+T](h: T, t: ListNode[T]) {
def head: T = h
def tail: ListNode[T] = t
def prepend[A >: T](elem: A): ListNode[A] = ListNode(elem, this)
}
参数类型 A
是一个新的类型参数,其下界为 T
。