Объясни вариантность в Kotlin
Вопрос
Объясни, что такое инвариантность, ковариантность и контравариантность в Kotlin, с примерами из stdlib.
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
По умолчанию дженерики инвариантны, и это защищает от записи чужого типа
`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 ошибок.
Ещё по теме «Дженерики и вариантность»
- Проверка is List<String> и стирание типов
- Вариантность в объявлении и в месте использования
- Как обойти стирание типов
- Инвариантность, out и in
- Контравариантность in T
- Конфликт перегрузок из-за стирания
- Почему List<String> можно передать как List<Any>
- Дженерики Kotlin и Java