Ответ вслух Senior Дженерики и вариантность обновлено

Объясни вариантность в Kotlin

Вопрос

Объясни, что такое инвариантность, ковариантность и контравариантность в Kotlin, с примерами из stdlib.

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

По умолчанию дженерик-класс инвариантен: MutableList<String не является MutableList<Any, и это правильно, иначе через ссылку на MutableList<Any можно было бы положить число в список строк.

Что должно прозвучать в ответе

  • По умолчанию дженерики инвариантны, и это защищает от записи чужого типа

  • `out`: производитель, сохраняет подтипизацию, пример `List`

  • `in`: потребитель, переворачивает подтипизацию, пример `Comparator`

  • Use-site проекции: `Array<out Any>`, аналог wildcards Java

  • Star projection `*` для неизвестного типа

  • Компилятор проверяет позиции: out только в возвращаемых значениях

Что могут спросить следом

  • Почему emptyList() можно присвоить в List<String>?
  • Что такое @UnsafeVariance?
  • Как это связано с PECS?

Разбор

По умолчанию дженерик-класс инвариантен: MutableList<String> не является MutableList<Any>, и это правильно, иначе через ссылку на MutableList<Any> можно было бы положить число в список строк. Если класс только отдаёт значения типа T, его объявляют out T, это ковариантность: List<String> подтип List<Any>, потому что у List нет методов записи. Если класс только принимает T, его объявляют in T, это контравариантность, подтипизация переворачивается: Comparator<Any> можно использовать как Comparator<String>. Компилятор следит за позициями: out-тип нельзя поставить в параметр метода. Для инвариантных классов вариантность указывают в месте использования, Array<out Any>, это аналог ? extends в Java, а Kotlin-подход в объявлении избавляет от wildcards в каждой сигнатуре. Star projection List<*> значит неизвестный тип: читать как Any?, писать нельзя. Красивое следствие: emptyList() имеет тип List<Nothing>, а так как Nothing подтип всего и List ковариантен, он подходит под любой List<T>.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.