为什么类型参数绑定 >: 的方法允许子类型?

2023-12-05

考虑以下 Scala 中堆栈的简单实现:

abstract class Stack[+A] {
  def top: A
  def pop: Stack[A]
}

case object EmptyStack extends Stack[Nothing] {
  def top = error("EmptyStack.top")
  def pop = error("EmptyStack.pop")
}

case class NonEmptyStack[A](elem: A, rest: Stack[A]) extends Stack[A] {
  def top = elem
  def pop = rest
}

现在假设我们要添加一个push方法Stack。 天真的尝试

abstract class Stack[+A] {
  def push(x: A): Stack[A] = new NonEmptyStack[A](x, this)
  ...
}

失败是因为A in (x: A)是一个逆变位置。 在Scala 示例第 58 页,作者建议

def push[B >: A](x: B): Stack[B] = new NonEmptyStack[B](x, this)

这里的类型绑定意味着给定一个特定类型的堆栈,我们可以推送相等或相同的对象更一般类型到该堆栈,结果是更通用类型的堆栈。

例如,

class Fruit
class Apple extends Fruit
class Banana extends Fruit

val apple = new Apple
val banana = new Banana

val stack1 = EmptyStack.push(apple)  // Stack[Apple]
val stack2 = stack1.push(banana)     // Stack[Fruit]

我认为这个选择的重点在于它真正保持了协方差Stack:如果一段代码需要一个Stack[Fruit]它将把任何水果(香蕉或苹果)推到上面,然后它仍然可以将这些水果推到Stack[Apple].

令人惊讶的是,我们还可以推送子类型:

class Honeycrisp extends Apple

val honeycrisp = Honeycrisp
val stack1 = EmptyStack.push(apple)  // Stack[Apple]
val stack2 = stack1.push(honeycrisp) // Stack[Apple], why does this work?

为什么这是允许的? 类型没有绑定>:意味着只允许超类型?


def push[B >: A](x: B): Stack[B] = ...

...

为什么这是允许的?类型没有绑定>:意味着只允许超类型?

仅允许超类型B,在你的例子中Apple. But x: B处于逆变(输入)位置,因此您始终可以传递更具体的值作为参数。这与定义无关B。然而,您将看到的是推断的类型honeycrisp is Apple not Honeycrisp.

这确实令人困惑,我记得曾经想过这个问题。但如果你仔细研究一下其中的含义,你会发现它确实保留了类型的健全性。当然,从身体的角度来看,push, x是真的Any没有可以信赖的特定功能。


可能相关:

  • scala 类型的上限和下限
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么类型参数绑定 >: 的方法允许子类型? 的相关文章

随机推荐