Scala поддерживает использование параметров типов для реализации классов и функций, которые могут поддерживать несколько типов. Параметры типа очень полезны при создании универсального . Однако в расширенных вариантах использования может потребоваться указать ограничения на используемые типы. Вот где различия помогают моделировать отношения между универсальными типами. Этот пост будет посвящен теме различий типов в Scala.
Что такое Дисперсия?
Дисперсия – это соотношение отношений подтипов сложных типов и отношений подтипов их составных типов. Дисперсия – это соотношение отношений подтипов сложных типов и отношений подтипов их составных типов.
Другими словами, дисперсия позволяет разработчикам моделировать отношения между типами и типами компонентов. Как следствие, дисперсия позволяет создавать чистые и очень удобные универсальные классы, которые широко используются в различных библиотеках Scala.
Scala поддерживает три типа дисперсии: ковариация , инвариантность и контравариантность . Имея это в виду, давайте рассмотрим каждый из них более подробно.
Ковариация
Давайте предположим, что у нас есть следующая структура классов
abstract class Vehicle { def name: String } case class Car(name: String) extends Vehicle case class Bus(name: String) extends Vehicle class VehicleList[T](val vehicle: T)
Автомобиль
и Классы Bus
оба наследуются от абстрактного класса Транспортное средство
. Учитывая, что у автомобиля класса есть своя коллекция Список транспортных средств[Автомобиль]
: является ли Список транспортных средств[Автомобиль]
подтипом Список транспортных средств[Автомобиль]
? Мой ответ – нет. Несмотря на то, что Автомобиль
класс расширяет Транспортное средство
класс, этого нельзя сказать о Список транспортных средств[Автомобиль]
и Список транспортных средств [Транспортное средство]
.
val vehicleList: VehicleList[Vehicle] = new VehicleList[Car](new Car("bmw")) // incorrect: type mismatch
Решение этой проблемы заключается в использовании ковариации . Как следствие, Список транспортных средств [Автомобиль]
будет подтипом Список транспортных средств [Транспортное средство]
. Это обеспечивает больший полиморфизм с универсальными типами. Чтобы составить Список транспортных средств
класс T
параметр ковариантный, просто добавьте небольшой знак + вдоль типа:
class VehicleList[+T](val vehicle: T) val vehicleList: VehicleList[Vehicle] = new VehicleList[Car](new Car("bmw")) // correct
Контравариантность
В отличие от ковариации, контравариантность допускает общий тип T
быть T
или супертип T
. Например, давайте рассмотрим пример класса, аналогичного тому, который использовался выше:
class CarList[T](val car: T)
Класс CarList
ожидает получить параметр типа T
и никакого другого супер- или подтипа. Что делать, если мы хотим использовать Список автомобилей[Автомобиль]
класс для хранения экземпляров класса транспортных средств, а также?
val carList: CarList[Car] = new CarList[Vehicle](new Vehicle("boat")) // incorrect: type mismatch
Чтобы решить эту проблему, вместо знака + в определении типа используйте знак -.
class CarList[-T](val car: T) val carList: CarList[Car] = new CarList[Vehicle](new Vehicle("boat")) // correct
Инвариантность
Инвариантность – это отношение универсального типа по умолчанию T
. Список [Автомобиль]
принимает только Автомобиль
тип – любой супертип или подтип не принимается.
class List[T](val t: T)
Резюме
Подводя итог, система Scala и дисперсий является очень удобным инструментом для моделирования отношений между универсальными типами. Я надеюсь, что этот пост помог вам лучше понять это, так что вы можете начать использовать его прямо сейчас!
Оригинал: “https://dev.to/bartoszgajda55/variances-in-scala-e8o”