{{3}}的最高答案解释了为什么方法参数是互变的。作者说,如果这样可以编译:
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)
}
那没关系:
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]
请注意,我已删除了原始代码段的最后一行。
我的思考过程如下:
ListNode[String]
的{{1}}被分配了一个新的list1
。这里没什么奇怪的。 ListNode(someString, null)
不是list1
。ListNode[String]
被分配了ListNode[Any]
。这很好,因为list1
是协变的,而ListNode
是Any
的超类型。String
的{{1}}方法。由于prepend
是list2
,因此list2
应该返回ListNode[Any]
。但是方法调用的结果已分配给list2.prepend()
。这可能无法编译,因为ListNode[Any]
是ListNode[Int]
的子类型,而Int
却不是协变的!我误解了吗?作者如何宣称将进行编译?
答案 0 :(得分:-1)
让我们考虑一下,如果我们定义如下:
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)
}
如果您查看prepend
方法,则以T
(它是ListNode的类型参数)作为参数,然后返回ListNode[T]
,就这么简单。现在,让我们详细说明一下用法:
val list1: ListNode[String] = ListNode("abc", null)
在上述情况下,String
是null
的超类型,并且由于ListNode
被定义为covarient
,因此是正确的。
val list2: ListNode[Any] = list1
在第二种情况下,ListNode[String]
被分配给ListNode[Any]
,因为Any
是String
的超类型,这也是正确的。
val list3: ListNode[Int] = list2.prepend(1)
最后,在上述第三种情况下,我们要做的是在1
之前加上Int
。如果您查看方法prepend(elem: T): ListNode[T]
,我们将传递Int
作为elem
值的类型,并返回ListNode
类型的T
;在这种情况下,类型为ListNode
的{{1}}。因此,从调用Int
返回的值是list2.prepend(1)
类型。因此,ListNode[Int]
的上述执行也是可能的(理论上是正确的)。
但是,在scala中,您无法定义list3
类型的def prepend(elem: T): ListNode[T]
(您会遇到编译错误),因此您将永远无法执行{{ 1}}。但是,您可以使用下限,如下所示:
covariant
答案 1 :(得分:-3)
单独考虑prepend
方法:
def prepend(elem: T): ListNode[T]
此签名意味着,如果您给它一个T
,则会得到ListNode[T]
。如果您给它Int
,则会得到ListNode[Int]
,因此您所质疑的作业是完全有效的。
还请记住,作者说ListNode
的这个版本未编译,所以这是一种假设情况。