Kotlin Academy · Lektion

Varians vid deklaration

in och out

Lektion 2 av 413 steg

Varians vid deklaration är en gratis lektion i Kotlin Academy på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Kotlin Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Kotlin Academy innehåller totalt 4 lektioner.

Variansproblemet

Är en List<String> en sorts List<Any>? Ibland ja, ibland nej. Varians beskriver hur generiska typer förhåller sig till varandra när deras typparametrar gör det. Kotlin använder out och in för att uttrycka detta.

Invarians som standard

Som standard är generiska typer invarianta: Box<String> är INTE en Box<Any>, även om String är en subtyp av Any. Det förhindrar osäkra operationer.

class Box<T>(val value: T)

fun main() {
    val strBox = Box("hi")
    // val anyBox: Box<Any> = strBox // would not compile
    println(strBox.value)
}

Kovarians med out

Om du markerar en typparameter med out blir den kovariant: Producer<String> blir en subtyp av Producer<Any>. Typen får bara förekomma i utdata-positioner, till exempel som returtyp.

class Producer<out T>(private val item: T) {
    fun produce(): T = item
}

fun main() {
    val strProducer: Producer<String> = Producer("hello")
    val anyProducer: Producer<Any> = strProducer
    println(anyProducer.produce())
}

Varför out är säkert

Om en klass bara producerar, alltså returnerar, T och aldrig konsumerar den, är det säkert att behandla en Producer<String> som en Producer<Any>, eftersom varje String som den returnerar också är en Any.

out i standardbiblioteket

Kotlins skrivskyddade List<out E> är kovariant, vilket är anledningen till att en List<String> kan användas där en List<Any> förväntas.

fun printAll(items: List<Any>) {
    items.forEach { println(it) }
}

fun main() {
    val names: List<String> = listOf("Ann", "Bob")
    printAll(names)
}

Kontravarians med in

Om du markerar en typparameter med in blir den kontravariant: Consumer<Any> blir en subtyp av Consumer<String>. Typen får bara förekomma i indata-positioner, till exempel som parametrar.

class Consumer<in T> {
    fun consume(item: T) {
        println("Consumed $item")
    }
}

fun main() {
    val anyConsumer: Consumer<Any> = Consumer()
    val strConsumer: Consumer<String> = anyConsumer
    strConsumer.consume("hi")
}

Varför in är säkert

En konsument som accepterar Any kan självklart acceptera en String. Därför kan en Consumer<Any> användas överallt där en Consumer<String> behövs.

PECS-minnesregeln

Kom ihåg Producer Extends, Consumer Super, eller uttryckt med Kotlin-termer: out för producenter, alltså utdata, och in för konsumenter, alltså indata. En typ som bara matar ut T använder out; en som bara tar emot T använder in.

Kombinera in och out

En typparameter som används för både indata och utdata, till exempel i en föränderlig lista, måste förbli invariant. Function-typer visar båda fallen: parametrar är in och returvärdet är out.

fun main() {
    val f: (Number) -> Int = { it.toInt() }
    val g: (Int) -> Number = f
    println(g(3.0.let { Number::class; 5 }))
}

Varians vid deklaration innebär en enda plats

Varians vid deklarationen anges en gång, på själva klassen med in/out. Varje användning av klassen får sedan variansen automatiskt, till skillnad från i Java där du upprepar wildcards vid varje användning.

Välj in eller out

Fråga dig: producerar klassen bara T, så att du ska använda out, konsumerar den bara T, så att du ska använda in, eller gör den båda delarna, så att den ska förbli invariant? Om modifieraren matchar rollen blir de generiska typerna flexibla och säkra.

Snabbkontroll

Vad anger modifieraren out?

Sammanfattning

Generiska typer är invarianta som standard. Använd out för kovarians när en typ bara producerar värden, och in för kontravarians när den bara konsumerar värden. Kom ihåg att producenter använder out och konsumenter använder in. Därefter får du tillämpa varians vid användningspunkten med typprojektioner.

Gratis att börja

Lär dig Kotlin med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
51
Lektioner
203

Vanliga frågor

Är lektionen ”Varians vid deklaration” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Kotlin Academy, inklusive ”Varians vid deklaration”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Kotlin Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Varians vid deklaration”?

in och out Ni övar på Kotlin Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Kotlin Academy?

Du behöver inga förkunskaper. Utbildningen i Kotlin Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Varians vid deklaration”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Kotlin Academy-lektionen?

Ja. Varje Kotlin Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Generiska funktioner och klasser
  2. Varians vid deklaration
  3. Varians vid användning
  4. Generiska begränsningar
← Tillbaka till Kotlin Academy