Sobes.tech
Junior — Middle

Czy możesz wyjaśnić koncepcję kontrawariancji w generykach Kotlin i jak wpływa ona na bezpieczeństwo typów?

sobes.tech AI

Odpowiedź od AI

Kontrawaryjność w Kotlinie jest oznaczana słowem kluczowym in i dotyczy parametrów typu w generykach, które są używane wyłącznie do przyjmowania wartości (parametry wejściowe). Oznacza to, że jeśli masz klasę Consumer<in T>, to Consumer<SuperType> może być przypisany do zmiennej typu Consumer<SubType>, czyli kierunek podstawiania typów jest odwrotny do dziedziczenia.

To pomaga zapewnić bezpieczeństwo typów, pozwalając na użycie bardziej ogólnego typu tam, gdzie oczekiwany jest bardziej szczegółowy, ale jednocześnie zabrania operacji, które mogą prowadzić do błędów w czasie działania.

Przykład:

interface Consumer<in T> {
    fun consume(item: T)
}

open class Animal
class Cat : Animal()

fun feedCat(consumer: Consumer<Cat>) {
    consumer.consume(Cat())
}

val animalConsumer: Consumer<Animal> = object : Consumer<Animal> {
    override fun consume(item: Animal) {
        println("Karmić zwierzę")
    }
}

// Dzięki kontrawaryjności można przekazać Consumer<Animal> tam, gdzie oczekuje się Consumer<Cat>
feedCat(animalConsumer)

Tutaj Consumer<Animal> może być używany jako Consumer<Cat>, ponieważ Consumer jest kontrawariantny względem typu T. Zapobiega to błędom związanym z niewłaściwym użyciem typów i pozwala na elastyczną pracę z typami generycznymi.