В то время как верхнее ограничение типа ограничивает тип до подтипа стороннего типа, нижнее ограничение типа объявляют тип супертипом стороннего типа. Термин B >: A
выражает, то что параметр типа B
или абстрактный тип B
относится к супертипу типа A
. В большинстве случаев A
будет задавать тип класса, а B
задавать тип метода.
Вот пример, где это полезно:
trait List[+A] {
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
}
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
trait List[+A]:
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
В данной программе реализован связанный список. Nil
представляет пустой список. Класс NonEmptyList
- это узел, который содержит элемент типа A
(head
) и ссылку на остальную часть списка (tail
). trait List
и его подтипы ковариантны, потому что у нас указанно +A
.
Однако эта программа не компилируется, потому что параметр elem
в prepend
имеет тип A
, который мы объявили ковариантным. Так это не работает, потому что функции контрвариантны в типах своих параметров и ковариантны в типах своих результатов.
Чтобы исправить это, необходимо перевернуть вариантность типа параметра elem
в prepend
. Для этого мы вводим новый тип для параметра B
, у которого тип A
указан в качестве нижней границы типа.
trait List[+A] {
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
}
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
trait List[+A]:
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
Теперь мы можем сделать следующее:
trait Bird
case class AfricanSwallow() extends Bird
case class EuropeanSwallow() extends Bird
val africanSwallows: List[AfricanSwallow] = Nil.prepend(AfricanSwallow())
val swallowsFromAntarctica: List[Bird] = Nil
val someBird: Bird = EuropeanSwallow()
// присвоить птицам (birds) африканских ласточек (swallows)
val birds: List[Bird] = africanSwallows
// добавляем новую птицу к африканским ласточкам, `B` - это `Bird`
val someBirds = africanSwallows.prepend(someBird)
// добавляем европейскую ласточку к птицам
val moreBirds = birds.prepend(EuropeanSwallow())
// соединяем вместе различных ласточек, `B` - это `Bird`, потому что это общий супертип для обоих типов ласточек
val allBirds = africanSwallows.prepend(EuropeanSwallow())
// но тут ошибка! добавление списка птиц слишком расширяет тип аргументов. -Xlint предупредит!
val error = moreBirds.prepend(swallowsFromAntarctica) // List[Object]
Параметр ковариантного типа позволяет birds
получать значение africanSwallows
.
Тип, связанный с параметром типа prepend
, позволяет добавлять различные разновидности ласточек и получать более абстрактный тип: вместо List[AfricanSwallow]
, мы получаем List[Bird]
.
Используйте -Xlint
, чтобы предупредить, если аргумент предполагаемого типа слишком абстрактен.
Contributors to this page:
Contents
- Введение
- Основы
- Единобразие типов
- Классы
- Значения Параметров По умолчанию
- Именованные Аргументы
- Трейты
- Кортежи
- Композиция классов с трейтами
- Функции Высшего Порядка
- Вложенные Методы
- Множественные списки параметров (Каррирование)
- Классы Образцы
- Сопоставление с примером
- Объекты Одиночки
- Регулярные Выражения
- Объект Экстрактор
- Сложные for-выражения
- Обобщенные Классы
- Вариантность
- Верхнее Ограничение Типа
- Нижнее Ограничение Типа
- Внутренние классы
- Члены Абстрактного Типа
- Составные Типы
- Самоописываемые типы
- Контекстные параметры, также известные, как неявные параметры
- Неявные Преобразования
- Полиморфные методы
- Выведение Типа
- Операторы
- Вызов по имени
- Аннотации
- Пакеты и Импорт
- Объекты Пакета