【发布时间】:2015-12-31 00:23:23
【问题描述】:
考虑以下 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
}
现在假设我们要向Stack 添加一个push 方法。
天真的尝试
abstract class Stack[+A] {
def push(x: A): Stack[A] = new NonEmptyStack[A](x, this)
...
}
失败是因为(x: A) 中的A 是一个逆变位置。
在Scala by Example第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?
为什么允许这样做?
类型绑定>:不是意味着只允许超类型吗?
【问题讨论】:
标签: scala type-bounds