Назад к вопросам
Junior
85
questionbank

Какие виды дженериков вы знаете?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Инвариантные, ковариантные и контравариантные.

  • Инвариантность: Тип List<A> не является подтипом List<B> и наоборот, даже если A является подтипом B. Тип сохраняется точно.
  • Ковариантность (out): Позволяет использовать подтипы. List<A> является подтипом List<B>, если A является подтипом B. Используется для выходных параметров (возвращаемых значений).
fun foo(list: List<out Number>) {
    // Можно читать из списка (Number, Double, Int и т.д.)
    val number: Number = list[0]
    // Нельзя добавлять в список (неизвестно, какого типа элементы ожидаются)
    // list.add(1) // Ошибка компиляции
}
  • Контравариантность (in): Позволяет использовать супертипы. List<B> является подтипом List<A>, если A является подтипом B. Используется для входных параметров (аргументов).
fun foo(comparable: Comparable<in String>) {
    // Можно передавать аргументы String (или его супертипы)
    val result = comparable.compareTo("hello")
    // Нельзя получать результаты типа String (неизвестно, какого типа результат)
    // val str: String = comparable.compareTo("world") // Ошибка компиляции
}

В Kotlin эти концепции реализуются с помощью аннотаций out и in на параметрах типов. В Java используется wildcards (? extends для ковариантности и ? super для контравариантности).

Тип Назначение Ключевое слово (Kotlin) Wildcard (Java) Разрешено чтение Разрешена запись
Инвариантный Точное совпадение типа Нет Нет Да Да
Ковариантный Использование подтипов (producer - "out") out ? extends T Да Нет
Контравариантный Использование супертипов (consumer - "in") in ? super T Нет Да