具体来说,看着getOrElse
。
Scala的Option
被定义在这样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
}
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
的约束(它应该返回选项的类型参数的超类型)。
不,这不是“一切顺利”,看看推断类型的返回值的接近。
声明B >: A
指:编译器将推断最具体类型B
使得到getOrElse
参数是类型B
并同时A
是B
的子类型。这是另一种说法:getOrElse
的返回类型是最上限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 C
的类型是C
,自此C <: B
,它也是类型B
的元素。没有矛盾:B
是推断返回类型,它不是最具体的参数类型default
的(这将是default.type
,直接使用时大多是无用的)。特别是,A
不必是default.type
的亚型,这将没有任何意义。
如果你想系统地做实验用A,B,C的组合,你会得到下面的返回类型:
| A B C
--+-----
A | A A A
B | A B B
C | A B C
这实质上是对全序设定元素C <: B <: A
“最大” - 函数。
简单直观和规则是:编译器和标准库的方法的函数签名确实努力,为客户提供最具体的返回类型,并尽可能多地保留类型信息成为可能。