«out» in Kotlin: Kovarianz anhand von Beispielen und Fragen verstehen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Was verbirgt sich hinter dem Wort out in Kotlin? Wir beantworten die häufigsten Fragen

\n\n

Wenn Sie jemals Generics in Kotlin geschrieben haben und auf einen unverständlichen Kompilierungsfehler gestoßen sind, haben Sie wahrscheinlich das Wort out gesehen. Dieser kleine Modifikator sorgt selbst bei erfahrenen Entwicklern für viele Diskussionen. Wir haben die häufigsten Fragen von Stack Overflow gesammelt und in eine verständliche Anleitung verwandelt. Heute erfahren Sie, was out ist, wofür es benötigt wird und wie Sie sich in der Kovarianz nicht verlieren.

\n\n

Lassen Sie es uns ein für alle Mal klären – ohne langweilige Theorie, nur mit lebendigem Code und Praxis.

\n\n

Warum hat Kotlin out erfunden? (Ein kurzer Ausflug in die Geschichte)

\n\n

In Java gibt es Wildcards – das Fragezeichen ? extends T. Es bedeutet, dass eine Sammlung Objekte vom Typ T oder dessen Untertypen enthalten kann. Das Problem ist, dass Java Sie nicht daran hindert, versehentlich ein falsches Objekt in eine solche Sammlung zu legen – Sie erhalten den Fehler erst zur Laufzeit. Kotlin hat dieses Problem auf Typebene gelöst.

\n\n

Das Schlüsselwort out in Kotlin ist eine Deklaration von Kovarianz. Wenn Sie Source<out T> schreiben, bedeutet das: „Ich kann Objekte vom Typ T aus dieser Source lesen, aber nicht in sie schreiben“. Der Compiler überwacht dies streng.

\n\n

Frage 1: Was ist der Unterschied zwischen out und in?

\n\n

Die häufigste Frage auf Stack Overflow. Stellen wir uns eine Kiste vor:

\n\n
    \n
  • out – eine Kiste, aus der man nur Dinge herausnehmen kann (produziert).
  • \n
  • in – eine Kiste, in die man nur Dinge hineinlegen kann (konsumiert).
  • \n
\n\n

Beispiel:

\n\n
// Kovarianz: können lesen, aber nicht schreiben\ninterface Producer<out T> {\n    fun produce(): T\n}\n\n// Kontravarianz: können schreiben, aber nicht lesen\ninterface Consumer<in T> {\n    fun consume(item: T)\n}\n\nfun main() {\n    val stringProducer: Producer<String> = object : Producer<String> {\n        override fun produce(): String = "Hallo"\n    }\n    \n    val anyProducer: Producer<Any> = stringProducer // Funktioniert! out erlaubt es\n    println(anyProducer.produce())\n}\n
\n\n

Beachten Sie: Producer<out T> erlaubt es, Producer<String> einer Variable vom Typ Producer<Any> zuzuweisen. Ohne out wäre das unmöglich.

\n\n

Frage 2: Warum verbietet Kotlin fun set(value: T) bei out?

\n\n

Das ist eine klassische Falle. Wenn Sie out deklariert haben, erlaubt der Compiler keine Funktionen, die T als Parameter akzeptieren. Warum?

\n\n

Stellen Sie sich vor, Sie haben eine MutableList<out Number>. Sie könnten ein Int hineinlegen, aber der Compiler weiß nicht, um welche Liste es sich tatsächlich handelt. Vielleicht ist es eine MutableList<Double>? Wenn das Schreiben erlaubt wäre, würden Sie die Typen brechen. out garantiert Sicherheit: Sie können nur lesen.

\n\n
// Wird nicht kompiliert!\ninterface BadList<out T> {\n    fun get(index: Int): T  // OK\n    fun add(item: T)        // Fehler! T befindet sich in einer in-Position\n}\n
\n\n

Lösung: Wenn Sie sowohl lesen als auch schreiben müssen, verwenden Sie in und out getrennt oder einen invarianten Typ (ohne Modifikator).

\n\n

Frage 3: Wie hängt out mit dem Wildcard ? extends aus Java zusammen?

\n\n

Direkte Entsprechung: Source<out T> in Kotlin = Source<? extends T> in Java. Aber es gibt einen wichtigen Unterschied:

\n\n
    \n
  • In Java ist das Wildcard eine Verwendung des Typs (use-site variance).
  • \n
  • In Kotlin ist out eine Deklaration des Typs (declaration-site variance).
  • \n
\n\n

Beispiel in Java:

\n\n
// Java: Wildcard bei der Variablendeklaration verwenden\nList<? extends Number> numbers = new ArrayList<Integer>();\nnumbers.a                

Blogs

Buchempfehlungen