Нормальный ответ на этот вопрос заключается в том, что изменчивость в сочетании с ковариацией может нарушить безопасность типов.Для коллекций это может быть принято как фундаментальная истина.Но теория на самом деле применима к любому универсальному типу, а не только к коллекциям, таким как List
и Array
, и нам вовсе не нужно пытаться рассуждать об изменчивости.
Реальный ответ связан скак типы функций взаимодействуют с подтипами.Коротко говоря, если параметр типа используется в качестве возвращаемого типа, он является ковариантным.С другой стороны, если параметр типа используется в качестве типа аргумента, он является контравариантным.Если он используется как в качестве возвращаемого типа, так и в качестве типа аргумента, он является инвариантом.
Давайте рассмотрим документацию для Array[T]
.Два очевидных метода, на которые следует обратить внимание, - это те, которые предназначены для поиска и обновления:
def apply(i: Int): T
def update(i: Int, x: T): Unit
В первом методе T
- это тип возвращаемого значения, а во втором T
- это тип аргумента.Дисперсионные правила предписывают, что T
должен быть поэтому инвариантным.
Мы можем сравнить документацию для List[A]
, чтобы понять, почему она ковариантна.Смущает, что мы нашли бы эти методы, которые аналогичны методам для Array[T]
:
def apply(n: Int): A
def ::(x: A): List[A]
Поскольку A
используется как тип возвращаемого значения и как тип аргумента, мы ожидаем A
быть инвариантным, как T
для Array[T]
.Однако, в отличие от Array[T]
, документация нам врет о типе ::
.Ложь достаточно хороша для большинства вызовов этого метода, но недостаточно хороша, чтобы определить дисперсию A
.Если мы развернем документацию для этого метода и нажмем «Полная подпись», нам будет показана правда:
def ::[B >: A](x: B): List[B]
Так что A
фактически не отображается как тип аргумента.Вместо этого B
(который может быть любым супертипом A
) является типом аргумента.Это не накладывает никаких ограничений на A
, поэтому действительно может быть ковариантным.Любой метод в List[A]
, который имеет A
в качестве типа аргумента, является аналогичной ложью (мы можем сказать, что эти методы помечены как [use case]
).