Почему метод с параметром типа bound>: разрешает подтипы?
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?
Почему это разрешено? Разве ограничение типа не >:
означает, что разрешены только супертипы?
Ответы (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Вопросы из категории :
- scala Есть ли реальный опыт использования программной транзакционной памяти?
- scala Преобразование коллекции Java в коллекцию Scala
- scala Что означает один апостроф в Scala?
- scala Самый масштабируемый веб-стек для высокопроизводительного приложения Flash / Flex / AIR?
- scala Разбиение макета Scala на инфиксном операторе
- scala Как мне обойти стирание типа на Scala? Или, почему я не могу получить параметр типа моих коллекций?
- scala Перегрузка конструктора Scala?
- scala Scala-эквивалент Java java.lang.Class <T> Object
- scala Синтаксис Скала точка (или его отсутствие)
- scala Предпочтительный способ создания списка Scala
- type-bounds Каков правильный способ реализации признаков с генериками в Scala?
- type-bounds Почему метод с параметром типа bound>: разрешает подтипы?
- type-bounds Scala Абстрактные члены типа - наследование и границы типов
- type-bounds Границы параметра типа - неопределяемое переопределение: возвращаемый тип требует непроверенного преобразования
- type-bounds Полиморфный родительский производный тип
- type-bounds How do I match types that don't have a specific typeclass instance?
- type-bounds Как добавить ограничение, что один универсальный тип реализует другой универсальный тип в Rust?
- type-bounds Типы Scala не работают с абстрактным классом, но работают с чертами
- type-bounds How to set lower-bound to type parameters in TypeScript?
- type-bounds Что делает пункт «где» внутри черты?