【问题标题】:How to understand the use of supertypes and variance in Scala's Option?如何理解 Scala 的 Option 中超类型和方差的使用?
【发布时间】:2018-09-17 22:49:41
【问题描述】:

具体来说,看getOrElse

Scala 的 OptionA 中被定义为协变的,如下所示:

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

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

【问题讨论】:

  • 把它想象成三元运算符 /if (c) new A else new B
  • 那么它继承了左值的类型(如果有的话)?好吧,有点道理。但是鉴于 getOrElse 的约束(它应该返回 Option 类型参数的超类型),我仍然不明白如何允许 optB.getOrElse(new C)。我会修改我的问题。
  • 好吧,如果您查看打印的 res25 的类型,它会说类型是 B,这似乎是完美的。其操作数的最大通用超类型。
  • expr new C 其类型为 C 被隐式转换为类型 B
  • 啊,我明白了,只是使用任何类型都可以毫无问题地被视为自身的超类型的基本属性。

标签: scala types scala-collections variance type-bounds


【解决方案1】:

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

声明B >: A 意味着:编译器将推断出最具体的类型B,这样getOrElse 的参数是B 类型,同时AB 的子类型。这是另一种说法: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 CC 类型,并且因为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 的全序集上的“最大”函数。

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

【讨论】:

  • 我不明白总订购量A <: B <: C。这是否读取 A 是 B 的子类型是 C 的子类型?如果是这样,那怎么可能?如果 C 扩展了 B 并且 B 扩展了 A,那么这是否意味着 C 是 B 的子类型而 B 是 A 的子类型,这会使排序 C <: B <: A
  • @DaltonSweeney 是的,你是对的,我出于某种原因颠倒了A, B, C 的顺序。这里,CB 的子类型,BA 的子类型,而不是相反。固定的。谢谢!
猜你喜欢
  • 2015-05-03
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 2016-01-02
  • 2019-08-27
  • 1970-01-01
  • 2021-01-24
  • 2013-05-31
相关资源
最近更新 更多