为什么类型参数绑定的方法 >: 允许子类型? [英] Why does a method with type parameter bound >: allow subtypes?
问题描述
考虑以下 Scala 中堆栈的简单实现:
Consider the following simple implementation of a stack in 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
方法.天真的尝试
Now suppose we want to add a push
method to Stack
.
The naive attempt
abstract class Stack[+A] {
def push(x: A): Stack[A] = new NonEmptyStack[A](x, this)
...
}
失败,因为 (x: A)
中的 A
是逆变位置.在 Scala 示例 第 58 页中,作者建议
fails because the A
in (x: A)
is a contravariant position.
In Scala by Example page 58, the author suggests
def push[B >: A](x: B): Stack[B] = new NonEmptyStack[B](x, this)
这里的类型绑定意味着给定一个特定类型的栈,我们可以将一个相等或者更通用类型的对象压入该栈中,结果是一个更通用类型的栈.
The type bound here means that given a stack of a certain type, we can push objects of an equal or more general type to that stack, and the result is a stack of the more general type.
例如
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]
.
I think the point of this choice is that it truly maintains the covariance of Stack
: if piece of code expects a Stack[Fruit]
onto which it will push any fruit (banana or apple), then it can still push those fruits onto a Stack[Apple]
.
令人惊讶的是,我们还可以推送子类型:
Surprisingly, we can also push subtypes:
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?
为什么允许这样做?类型绑定 >:
不是意味着应该只允许超类型吗?
Why is this allowed?
Doesn't the type bound >:
mean that only supertypes should be allowed?
推荐答案
def push[B >: A](x: B): Stack[B] = ...
...
为什么允许这样做?类型绑定 >:
不是意味着应该只允许超类型吗?
Why is this allowed? Doesn't the type bound >:
mean that only supertypes should be allowed?
仅允许超类型作为 B
,在您的示例 Apple
中.但是 x: B
处于逆变(输入)位置,因此您始终可以将更具体的值作为参数传递.这与B
的定义无关.但是,您将看到 honeycrisp
的推断类型是 Apple
而不是 Honeycrisp.
Only super-types are allowed as B
, in your example Apple
. But x: B
is in a contravariant (input) position, and as such you can always pass a more specific value as an argument. That has nothing to do with the definition of B
. What you will see however is that the inferred type for honeycrisp
is Apple
not Honeycrisp.
这确实令人困惑,我记得曾经想过这个问题.但是,如果您仔细阅读这些含义,它确实保留了类型的完整性.当然,因此,从 push
的主体的角度来看,x
确实是 Any
,没有它可以依赖的特定功能.
This is indeed perplexing, and I remember having wondered about this once. But if you go through the implications, it really preserves the type soundness. Of course as a consequence, from the point of view of the body of push
, x
is really Any
without specific capabilities it could count on.
可能相关:
这篇关于为什么类型参数绑定的方法 >: 允许子类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!