2

具体来说,看getOrElse.

ScalaOption被定义为协变的,A如下所示:

sealed abstract class Option[+A] extends Product with Serializable {
  self =>
  @inline final def getOrElse[B >: A](default: => B): B =
    if (isEmpty) default else this.get
}

of 的定义getOrElse似乎暗示A必须返回一个超类型,这对我来说不一定很有意义。但实际上,它看起来像任何事情:子类型或超类型。

scala> class A
// defined class A
scala> class B extends A
// defined class B
scala> val optA: Option[A] = Option(null)
val optA: Option[A] = None
scala> optA.getOrElse(new B)
val res23: A = B@66a2c8e7
scala> class C extends B
// defined class C
scala> val optB: Option[B] = Option(null)
val optB: Option[B] = None
scala> optB.getOrElse(new A)
val res24: A = A@2a460bf
scala> optB.getOrElse(new C)
val res25: B = C@e87f97f

考虑到限制,这怎么可能?具体来说,我不明白optB.getOrElse(new C)在给定约束的情况下如何允许getOrElse(它应该返回 Option 类型参数的超类型)。

4

1 回答 1

1

不,这不是“一切顺利”,请仔细查看返回值的推断类型。

声明的B >: A意思是:编译器将推断出最具体的类型B,使得 to 的参数getOrElse是类型B并且同时A是 的子类型B。这是另一种说法:返回类型getOrElse是参数 of和后备参数类型的最小上限。Option

您的实验证实了这一点:

scala> class A
scala> class B extends A
scala> class C extends B

scala> val optA: Option[A] = Option(null)
scala> optA.getOrElse(new B)
val res23: A = B@66a2c8e7                 // LUB(A, B) = A

scala> val optB: Option[B] = Option(null)
scala> optB.getOrElse(new A)
val res24: A = A@2a460bf                  // LUB(B, A) = A, symmetric!

scala> optB.getOrElse(new C)
val res25: B = C@e87f97f                  // LUB(B, C) = B

最后一种情况当然是完全有效的,因为new Cis 是 type ,并且CsinceC <: B也是type 的一个元素。没有矛盾:是推断的返回类型,它不是参数的最具体类型(这将是,直接使用时几乎没用)。特别是,不必是 的子类型,这没有任何意义。BBdefaultdefault.typeAdefault.type

如果您系统地使用 A、B、C 的组合进行所有实验,您将获得以下返回类型:

  | A B C
--+-----
A | A A A
B | A B B
C | A B C

这本质上是带有 elements 的完全有序集合上的“最大”函数C <: B <: A

简单直观的规则是:标准库中方法的编译器和函数签名非常努力地提供最具体的返回类型,并尽可能多地保留类型信息。

于 2018-04-08T15:23:05.513 回答