Почему метод с параметром типа bound>: разрешает подтипы?

scala type-bounds

120 просмотра

1 ответ

Рассмотрим следующую простую реализацию стека в 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в (x: A)это противоречивая позиция. В Scala by Example page 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]что он вытолкнет какой-либо фрукт (банан или яблоко), то он все равно может подтолкнуть эти фрукты к a 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?

Почему это разрешено? Разве ограничение типа не >:означает, что разрешены только супертипы?

Автор: DanielSank Источник Размещён: 12.11.2019 09:14

Ответы (1)


3 плюса

Решение
def push[B >: A](x: B): Stack[B] = ...

...

Почему это разрешено? Разве ограничение типа не >:означает, что разрешены только супертипы?

В Bвашем примере допускаются только супертипы Apple. Но x: Bнаходится в контравариантной (входной) позиции, и в качестве таковой вы всегда можете передать более конкретное значение в качестве аргумента. Это не имеет ничего общего с определением B. То , что вы будете видеть , однако, что выведенный тип для honeycrispэто AppleнеHoneycrisp.

Это действительно сбивает с толку, и я помню, как однажды задумался об этом. Но если вы пройдете через последствия, это действительно сохранит правильность типа. Конечно, как следствие, с точки зрения совокупности push, xна самом деле Anyнет конкретных возможностей, на которые он мог бы рассчитывать.


Возможно связано:

Автор: 0__ Размещён: 30.12.2015 03:55
Вопросы из категории :
32x32