Generics в Kotlin позволяют создавать универсальные и типобезопасные структуры данных и функции. Основная особенность: Kotlin реализует систему generic типов на уровне компиляции, как и Java, но с более строгой типизацией и расширенным синтаксисом variance (ковариантность и контравариантность).
Ограничения дженериков:
T() запрещено).Variance:
Пример ковариантности:
interface Producer<out T> { fun produce(): T }
Пример контравариантности:
interface Consumer<in T> { fun consume(item: T) }
Отличие от Java:
out вместо ? extends, in вместо ? super).?, только in/out).Вопрос: "Можно ли в Kotlin объявить массив масивов (Array<Array<Int>>) как Array<out Array<Int>> и что произойдет при попытке записи в такой массив?"
Ответ: Да, можно объявить как Array<out Array<Int>>, но такой массив становится только для чтения (read-only):
val arr: Array<out Array<Int>> = Array(1) { Array(1) { 0 } } arr[0] = arrayOf(1, 2, 3) // Ошибка компиляции!
Попытка записать значение приведёт к ошибке — generic array с out-параметром не позволяет записывать элементы, потому что тогда нарушилась бы типобезопасность.
История
В команде пытались создать массив generic-объектов с типом out-параметра, а после — положить в него значения через set(index, value). Код компилировался, но вызывал ошибку в рантайме, и несколько функций оказались неработоспособны.
История
Однажды при миграции библиотеки с Java на Kotlin оставили wildcard-типы (? extends ...), а в Kotlin просто скопировали типы без изменения на out/in. Результат — компиляция не прошла, а при "обходе" ошибка пошла в рантайме, усложнив процесс дебага.
История
Использовали in/out variance с пользовательским классом, но перепутали модификаторы, объявив interface Stack<in T> вместо Stack<out T>. Это привело к невозможности возвращать элементы из стека: сигнатура метода нарушала system out/in contract.