【问题标题】:Why can the ListNode example (Scala website) handle different types?为什么 ListNode 示例(Scala 网站)可以处理不同的类型?
【发布时间】:2012-07-01 09:13:58
【问题描述】:

ListNode example,取自 Scalas 主页,如下所示:

case class ListNode[+T](h: T, t: ListNode[T]) {
  def head: T = h
  def tail: ListNode[T] = t
  def prepend[U >: T](elem: U): ListNode[U] =
    ListNode(elem, this)
}

通过这个类,我们可以创建如下对象:

val empty: ListNode[Null] = ListNode(null, null)
val strList: ListNode[String] = empty.prepend("hello")
                                     .prepend("world")
val anyList: ListNode[Any] = strList.prepend(12345)

如我们所见,我们可以在String 节点前添加一个整数值。我想,这是因为类型参数U 将自动设置为Any,当给prepend 方法提供整数时(因为Int 不是String 的超类型)。

当尝试使用自己的下限示例时,我会得到一个错误:

scala> class E[T >: String]
defined class E

scala> new E[Any]
res1: E[Any] = E@135f0a

scala> new E[Int]
<console>:11: error: type arguments [Int] do not conform to class E's type param
eter bounds [T >: String]
      val res2 =
          ^
<console>:12: error: type arguments [Int] do not conform to class E's type param
eter bounds [T >: String]
              new E[Int]
                  ^

为什么这里的Int 类型不会像ListNode 示例中那样自动被视为Any 类型?


更新 1:这也有效(没有明确说明新的 ListNode 应该是 Any 类型)

scala> val empty: ListNode[Null] = ListNode(null, null)
empty: example.listNode.ListNode[Null] = ListNode(null,null)

scala> empty.prepend("hello").prepend("world")
res0: example.listNode.ListNode[java.lang.String] = ListNode(world,ListNode(hell
o,ListNode(null,null)))

scala> val strList: ListNode[String] = empty.prepend("hello").prepend("world")
strList: example.listNode.ListNode[String] = ListNode(world,ListNode(hello,ListN
ode(null,null)))

scala> strList.prepend(12345)
res1: example.listNode.ListNode[Any] = ListNode(12345,ListNode(world,ListNode(he
llo,ListNode(null,null))))

【问题讨论】:

    标签: scala type-parameter lower-bound


    【解决方案1】:

    您收到上述错误是因为Int 不是String 的超类型。

    请注意,在上面的 ListNode 代码中,String 是 Null 的超类型(请参阅 class hierarchy),Any 是 String 的超类型(正如您正确指出的那样)。

    我猜想混淆是由比较两个不太相同的操作引起的:new E[Int] 使用类型参数Int 实例化类,它不符合下限String,因此失败。

    另一方面,在上面的ListNode 代码中,您调用prepend 方法,该方法采用T 的超类型U。创建anyList 时,U (正如您所猜测的那样)解析为Any,因为这是String 和Int 的唯一常见超类型,因此您可以认为它并没有真正传递@987654341 @ 给它,但只是Any 的一些任意实例(它也恰好是Int 类型)。

    因此,

    val anyList: ListNode[Int] = strList.prepend(12345)
    

    同样失败,因为strList.prepend(12345) 只能返回ListNode[Any]。

    【讨论】:

    • 谢谢。但是为什么'strList.prepend(12345)'行没有抛出错误,其中整数显然不是String的超类型?
    • 因为你生成了一个ListNode[Any],它符合下限。如果您尝试val anyList: ListNode[Int] = strList.prepend(12345),则会失败。
    • prepend 调用也可以在没有明确说明结果ListNode 应该是Any 类型的情况下工作。所以它背后似乎有一个自动设置?有关示例,请参阅我的问题的更新 1。
    • 这是因为您特别地使用类型参数Int 实例化E,这是无法做到的。简单地说,类型推断只填写空白,但new E[Int] 没有歧义,因此没有空白要填写(请参阅我的答案中的第 2 和第 3 段)。想象一下,你会说val x = new E[Int],然后才发现x 实际上不是那种类型。这会很奇怪,对吧?
    • 为了完整起见:您可以通过指定 U 应该解析的类型来“禁用”这种类型推断。只需在解释器中尝试strList.prepend[Int](12345)(失败)和strList.prepend[Any](12345)(有效)。
    【解决方案2】:

    您正在做一件根本不同的事情:传递Int 类型。让我们把原来的代码破解一下,例如:

    scala> val anyList: ListNode[Any] = strList.prepend[Int](12345)
    <console>:11: error: type arguments [Int] do not conform to method prepend's type parameter bounds [U >: String]
           val anyList: ListNode[Any] = strList.prepend[Int](12345)
                                                       ^
    

    看到了吗?如果你做指定Int,它会中断。它在原始版本中有效,因为 12345 被推断为类型 Any,而不是类型 Int。

    【讨论】:

    • 感谢您提供另一个示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多