Varians vid deklaration
in och out
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.
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
- Generiska funktioner och klasser
- Varians vid deklaration
- Varians vid användning
- Generiska begränsningar