如何理解 Scala 的 Option 中超类型和变体的使用?
How to understand the use of supertypes and variance in Scala's Option?
具体看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
的超类型必须 returned,这对我来说不一定很有意义。但事实上,看起来任何事情都会发生:子类型或超类型。
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
考虑到限制,这怎么可能?具体来说,鉴于 getOrElse
的约束,我不明白如何允许 optB.getOrElse(new C)
(它应该 return 选项类型参数的超类型)。
不,不是 "everything goes",仔细看看 return 值的推断类型。
声明 B >: A
意味着:编译器将推断最具体的类型 B
这样 getOrElse
的参数是 B
类型并且同时 A
是 B
的子类型。这是另一种说法:getOrElse
的return类型是Option
的参数和Option
的类型的最小上限 fallback-argument.
您的实验证实了这一点:
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
是推断的return类型,它不是参数的最具体类型default
(这将是 default.type
,直接使用时几乎没有用)。特别是,A
不必是 default.type
的子类型,这没有任何意义。
如果您系统地使用 A、B、C 的组合进行所有实验,您将得到以下 return 类型:
| A B C
--+-----
A | A A A
B | A B B
C | A B C
这本质上是 "maximum" 元素在完全有序集合上的函数 C <: B <: A
。
简单直观的规则是:编译器和标准库中方法的函数签名非常努力地提供最具体的 return 类型,并尽可能多地保留类型信息。
具体看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
的超类型必须 returned,这对我来说不一定很有意义。但事实上,看起来任何事情都会发生:子类型或超类型。
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
考虑到限制,这怎么可能?具体来说,鉴于 getOrElse
的约束,我不明白如何允许 optB.getOrElse(new C)
(它应该 return 选项类型参数的超类型)。
不,不是 "everything goes",仔细看看 return 值的推断类型。
声明 B >: A
意味着:编译器将推断最具体的类型 B
这样 getOrElse
的参数是 B
类型并且同时 A
是 B
的子类型。这是另一种说法:getOrElse
的return类型是Option
的参数和Option
的类型的最小上限 fallback-argument.
您的实验证实了这一点:
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
是推断的return类型,它不是参数的最具体类型default
(这将是 default.type
,直接使用时几乎没有用)。特别是,A
不必是 default.type
的子类型,这没有任何意义。
如果您系统地使用 A、B、C 的组合进行所有实验,您将得到以下 return 类型:
| A B C
--+-----
A | A A A
B | A B B
C | A B C
这本质上是 "maximum" 元素在完全有序集合上的函数 C <: B <: A
。
简单直观的规则是:编译器和标准库中方法的函数签名非常努力地提供最具体的 return 类型,并尽可能多地保留类型信息。