Generics
https://kotlinlang.org/docs/generics.html |
22.02.2018 | https://youtu.be/HLkjmO4_WeM |
Generics
Generics или обобщения представляют технику, посредством которой методы и классы могут использовать объекты, типы которых на момент определения классов и функций неизвестны. Обобщения позволяют определять шаблоны, в которые можно подставлять различные типы. Ограничения обобщений (generic constraints) ограничивают набор типов, которые могут передаваться вместо параметра в обобщениях. По умолчанию ко всем параметрам типа также применяется ограничение в виде типа Any?. То есть определение параметра типа фактически аналогично определению <T: Any?>.
open class Grandpa // Дед
open class Dad: Grandpa() // Батя
class Son: Dad() // Сынок
Covariance | Ковариантность | Если есть тип T (батя), то разрешены эти типы и его подтипы (сынок). | Есть налог на людей без внуков, платить батя и сынок. |
Contravariance | Контравариантность | Если есть тип T (батя), то разрешены эти типы и его супертипы (дед). | Только взрослые могут пить пивас, пьют батя и дед. |
Invariance | Инвариантность | Если есть тип T (батя), то разрешены эти типы и никаки другие. | Скидка для людей, у которых есть родители и дети, скидку получает только батя. |
Bivariance | Бивариантность | Если есть тип T (батя), то разрешены как подтипы, так и супертипы | Скидка для всех мужиков, скидку получают дед батя и сынок. |
out
Определяет ковариантность в месте объявления. Делает параметризованный тип ковариантным. Если T - Any, то разрешены также все наследники Any
interface Source<out T: Any> {
fun nextT(): T
}
fun demo(strings: Source<String>) {
val objects: Source<Any> = strings // Всё в порядке, т.к. T — out-параметр
}
val value: List<Any> = listOf(1, 2, 3) // since List signature is List<out T> in Kotlin
in
Определяет контравариантность в месте объявления. Делает параметризованный тип контравариантным. Если T - Double, то разрешен также Number
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 имеет тип Double, расширяющий Number
val y: Comparable<Double> = x // Можем присвоить значение x переменной типа Comparable<Double>
}
where
Используется для указания нескольких ограничений типов
class Person<T>(
val id: T,
val name: String
)
fun <T> display(obj: T) {
println(obj)
}
val tom: Person<Int> = Person(367, "Tom")
val bob: Person<String> = Person("A65", "Bob")
display("Hello Kotlin")
display(1234)
display(true)
fun <T: Comparable<T>> getBiggest(a: T, b: T): T {
return if (a > b) a else b
}
fun <T> getBiggest(a: T, b: T): T where T: Comparable<T>, T: Number {
return if (a > b) a else b
}
class Messenger<T> where T: Serializable, T: Parcelable {
fun convert(obj: T) {}
}
star-projection (*)
Звездная проекция. Используется в дженериках для указания, что вы не знаете или не хотите явно указывать точный тип параметра типа, но при этом хотите сохранить типовую безопасность. Звездная проекция фактически означает “любой допустимый тип” с определенными ограничениями.
• Для ковариантных типов (например, List<out T>
), List<*>
интерпретируется как List<out Any?>
. Это означает, что элементы можно безопасно получать (как Any?
), но нельзя изменять.
• Для контравариантных типов (например, Comparable<in T>
), Comparable<*>
интерпретируется как Comparable<Nothing>
. Это значит, что вы не можете передать в него значение, потому что нет допустимых значений для Nothing
.
• Для инвариантных типов (например, MutableList<T>
), MutableList<*>
интерпретируется так, что вы можете только получать элементы как Any?
, но не можете изменять список.
- Проекция для входных типов (как в
List<*>
)
Когда используется List<*>
, это эквивалентно List<out Any?>
. Это означает, что вы можете безопасно читать элементы как тип Any?
, но вы не можете добавлять элементы в этот список, так как компилятор не знает, какой конкретный тип должен быть в списке.
fun printContents(list: List<*>) {
for (item in list) {
println(item)
}
}
- Проекция для изменяемых коллекций (как в
MutableList<*>
)
Для изменяемых коллекций, таких как MutableList<*>
, звездная проекция запрещает как чтение с конкретным типом (кроме Any?
), так и запись в коллекцию. Это связано с тем, что тип элемента неизвестен, и компилятор не может гарантировать типовую безопасность.
val mutableList: MutableList<*> = mutableListOf(1, 2, 3)
val first = mutableList[0] // first имеет тип Any?
mutableList.add(4) // Ошибка компиляции: тип неизвестен, нельзя добавить элемент
Kotlin Generics. Вопросы на собесе
- Что такое дженерики (generics) в Kotlin?
- Объясните основные различия между дженериками в Kotlin и Java?
- Как Kotlin решает проблемы с «Type Erasure» (стиранье типов), которые существуют в Java?
- Что такое инвариантность, ковариантность и контравариантность?
- Объясните, что такое звездная проекция в Kotlin и где она используется?