Generics in Kotlin: Wie Sie die Angst vor spitzen Klammern verlieren und sauberen Code schreiben

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

Einleitung: Warum Generics nicht beängstigend, sondern praktisch sind

\n\n

Stellen Sie sich vor, Sie schreiben eine Klasse „Box“. Sie können alles hineinlegen: ein Buch, einen Apfel, ein Telefon. Aber wenn Sie sie für Object erstellen, müssen Sie bei jedem Herausnehmen as? Book verwenden und hoffen, dass Sie keinen ClassCastException erhalten. Kommt Ihnen das bekannt vor?

\n\n

Hier kommen Generics ins Spiel – ein leistungsstarker Typisierungsmechanismus in Kotlin, der es Ihnen ermöglicht, flexiblen Code zu schreiben, ohne die Typsicherheit zu opfern. In diesem Artikel behandeln wir Generics in Kotlin vom Einfachen zum Komplexen: grundlegende Klassen, Funktionen, Variance (in/out), reifizierte Typen und Star-Projektionen. Sie erfahren, wie Sie Ihren Code sauberer machen und den Compiler zu Ihrem wichtigsten Verbündeten machen.

\n\n

Dieser Artikel wurde für das Bildungsportal pythonlib.ru erstellt – Ihrem Wegweiser in die Welt der modernen Programmiersprachen.

\n\n

1. Grundlagen: Was sind Generics in Kotlin?

\n\n

Generics (generische Typen) ermöglichen es, Klassen, Schnittstellen und Funktionen mit einem Typ zu parametrisieren, der bei der Verwendung angegeben wird. Dies vermeidet Code-Duplizierung und gibt Ihnen Kontrolle über die Typen zur Kompilierzeit.

\n\n

Sehen Sie sich dieses einfache Beispiel an:

\n\n
class Box<T>(val item: T)\n\nfun main() {\n    val bookBox = Box<String>("Kotlin in Action")\n    val intBox = Box<Int>(42)\n    // Der Compiler kennt den Typ!\n    val title: String = bookBox.item\n}
\n\n

Wichtige Punkte:

\n
    \n
  • <T> – der Typparameter (kann jeder Buchstabe sein, aber üblich sind T, R, E, K, V).
  • \n
  • Der Typ wird bei der Objekterstellung festgelegt – keine Casts zur Laufzeit.
  • \n
  • Generics funktionieren nur zur Kompilierzeit (Type Erasure), aber Kotlin fügt „Reifikation“ hinzu (dazu später mehr).
  • \n
\n\n

Generische Funktionen

\n

Die Typisierung in Kotlin funktioniert auch für Funktionen. Zum Beispiel eine Funktion, die das erste Element einer Liste zurückgibt:

\n\n
fun <T> firstItem(list: List<T>): T? = list.firstOrNull()\n\nfun main() {\n    val numbers = listOf(1, 2, 3)\n    val first: Int? = firstItem(numbers) // T = Int\n    println(first) // 1\n}
\n\n

Beachten Sie: Kotlin kann den Typ aus dem Kontext ableiten – firstItem<Int>(numbers) zu schreiben ist nicht notwendig.

\n\n

2. Variance: in und out – wie man die Typsicherheit nicht bricht

\n\n

Das schwierigste Thema für Anfänger ist Variance (Varianz). Sie beantwortet die Frage: Kann List<String> ein Subtyp von List<Any> sein?

\n\n

In Java wird dies mit Wildcards gelöst (? extends / ? super). In Kotlin mit out und in.

\n\n

out (Kovarianz) – nur lesen

\n

Die Markierung out bedeutet, dass der generische Typ nur „herausgegeben“ (von Methoden zurückgegeben) werden kann. Beispiel: List<out T>.

\n\n
interface Source<out T> {\n    fun next(): T  // OK: T als Ausgabe\n    // fun add(item: T) // Fehler! T als Eingabe ist verboten\n}\n\nfun demo(source: Source<String>) {\n    val anySource: Source<Any> = source // out erlaubt dies\n}
\n\n

Warum ist das sicher? Wenn wir nur String aus der Quelle lesen können, kann sie als Quelle für Any betrachtet werden.

\n\n

in (Kontravarianz) – nur schreiben

\n

in ist das Gegenteil: Der Typ kann nur als Eingabe verwendet werden. Klassiker: Comparable.

\n\n
interface Comparable<in T> {\n    operator fun compareTo(other: T): Int\n}\n\nfun demo(comp: Comparable<Number>) {\n    val intComp: Comparable<Int> = comp // in erlaubt dies\n}
\n\n

Wenn Comparable<Number> beliebige Zahlen vergleichen kann, dann kann es definitiv auch Int vergleichen.

\n\n

in out (Invarianz) – Standard

\n

Wenn weder in noch out angegeben ist, ist der Typ inv

Blogs

Buchempfehlungen