为什么类型参数绑定的方法 >: 允许子类型? [英] Why does a method with type parameter bound >: allow subtypes?

查看:42
本文介绍了为什么类型参数绑定的方法 >: 允许子类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆