English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Generics in Kotlin

Generics, auch "parametrisierte Typen" genannt, parametrisieren Typen und können in Klassen, Interfaces und Methoden verwendet werden.

Wie in Java bietet Kotlin auch Generics, um Typsicherheit zu gewährleisten und die Unannehmlichkeiten der Typumwandlung zu beseitigen.

Eine generische Klasse wird deklariert:

class Box<T>(t: T) {
    var value = t
}

Bei der Erstellung von Beispielklassen müssen wir Typparameter angeben:

val box: Box<Int> = Box<Int>(1)
// oder
val box = Box(1) // Der Compiler führt Typinferenz durch1 Typ Int, daher weiß der Compiler, dass wir von Box<Int> sprechen.

Nachfolgender Beispielcode übergibt Integer-Daten und Strings an die generische Klasse Box:

class Box<T>(t: T) {
    var value = t
}
fun main(args: Array<String>) {
    var boxInt = Box<Int>(10)
    var boxString = Box<String>("w3codebox")
    println(boxInt.value)
    println(boxString.value)
}

Die Ausgabe ist:

10
w3codebox

Generische Typvariable können vollständig die Typparameter angeben, wenn der Compiler die Typparameter automatisch bestimmen kann, können die Typparameter auch weggelassen werden.

Die Deklaration generischer Funktionen in Kotlin ist wie in Java, die Typparameter müssen vor dem Funktionsnamen stehen:

fun <T> boxIn(value: T) = Box(value)
// Nachfolgende sind gültige Anweisungen
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // Der Compiler führt Typinferenz durch

Bei der Aufrufung generischer Funktionen kann der Typparameter weggelassen werden, wenn der Typparameter ermittelt werden kann.

Nachfolgender Beispielcode erstellt eine generische Funktion doPrintln, die entsprechend der übergebenen Typen behandelt:

fun main(args: Array<String>) {
    val age = 23
    val name = "w3codebox"
    val bool = true
    doPrintln(age)    // Integer
    doPrintln(name)   // String
    doPrintln(bool)   // Boolean
}
fun <T> doPrintln(content: T) {
    when (content) {
        is Int -> println("Ganzzahlige Nummern sind $content")
        is String -> println("String in Großbuchstaben umwandeln: ${content.toUpperCase()}")
        else -> println("T ist kein Integer und auch kein String")
    }
}

Die Ausgabe ist:

Ganzzahlige Nummern sind 23
String in Großbuchstaben umwandeln: w3codebox
T ist kein Integer und auch kein String

Generikklauelsen

Wir können Generikklauelsen verwenden, um festzulegen, welche Typen für einen bestimmten Parameter erlaubt sind.

In Kotlin wird das Typlimit der Generics mit einem : angegeben.

Die häufigste Einschränkung ist die obere Begrenzung (upper bound):

fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
}

Untertypen von Comparable können T ersetzen. Zum Beispiel:

sort(listOf(1, 2, 3)) // OK. Int ist der Untertyp von Comparable<Int>
sort(listOf(HashMap<Int, String>())) // Fehler: HashMap<Int, String> ist nicht der Untertyp von Comparable<HashMap<Int, String>>

Die Standardobergrenze ist Any?.

Für mehrere obere Begrenzungsbedingungen kann die where-Klausel verwendet werden:

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
    where T : CharSequence,
          T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}

Typveränderung

Kotlin hat keine Wildcard-Typen, es gibt zwei andere Dinge: Typveränderung am Deklarationsort (declaration-site variance) und Typprojektionen (type projections).

Typveränderung am Deklarationsort

Typveränderungen am Deklarationsort werden mit dem Covariantitätsanmerkungsmodifikator: in, out, Verbraucher in, Produzent out, markiert.

Verwenden Sie out, um einen Typparameter covariant zu machen. Covarianten Typparameter können nur als Ausgabetyp verwendet werden und können als Rückgabetyp dienen, aber nicht als Eingabetyp:

// Definieren Sie eine Klasse, die Covariantität unterstützt
class w3codebox<out A>(val a: A) {
    fun foo(): A {
        return a
    }
}
fun main(args: Array<String>) {
    var strCo: w3codebox<String> = w3codebox("a")
    var anyCo: w3codebox<Any> = w3codebox<Any>("b")
    anyCo = strCo
    println(anyCo.foo())   // Gib a aus
}

in macht einen Typparameter kontravariant. Kontravariante Typparameter können nur als Eingaben verwendet werden und können nicht als Rückgabetypen dienen:

// Definiere eine Klasse, die Invarianz unterstützt
class w3codebox<in A>(a: A) {
    fun foo(a: A) {
    }
}
fun main(args: Array<String>) {
    var strDCo = w3codebox("a")
    var anyDCo = w3codebox<Any>("b")
    strDCo = anyDCo
}

Stern-Projektion

Manchmal möchtest du möglicherweise angeben, dass du keine Informationen über die Typparameter hast, aber dennoch sicher damit umgehen möchtest. Unter "sicherem Umgang" wird hier eine Typprojektion für die generische Typdefinition verstanden, die alle konkreten Beispiele dieses Projekts als Untertypen erfordert.

Für dieses Problem bietet Kotlin eine Syntax an, die als Stern-Projektion (star-projection):

  • Wenn der Typ definiert ist als Foo<out T>, wobei T ein covarianter Typparameter ist und der obere Grenzwert (upper bound) TUpper ist, Foo<ist äquivalent zu Foo<out TUpper> . Es bedeutet, wenn T unbekannt ist, kannst du sicher aus Foo<liest Werte des Typs TUpper aus.

  • Wenn der Typ definiert ist als Foo<in T>, wobei T ein kontravarianter Typparameter ist, Foo<ist äquivalent zu Foo<in Nothing> . Es bedeutet, wenn T unbekannt ist, kannst du nicht sicher in Foo<Schreibt alles hinein.

  • Wenn der Typ definiert ist als Foo<T>, wobei T ein covarianter Typparameter ist und der obere Grenzwert (upper bound) TUpper ist, für den Fall der Lesoperationen, Foo<*ist äquivalent zu Foo<out TUpper> und für den Fall der Schreiboperationen äquivalent zu Foo<in Nothing> .

Wenn eine generische Typ in mehrere Typparameter besteht, kann jeder Typparameter einzeln projiziert werden. Zum Beispiel, wenn der Typ definiert ist als interface Function<in T, out U>, dann können die folgenden Stern-Projektionen auftreten:

  • Function<*, String> , stellt Function<in Nothing, String> dar;

  • Function<Int, *> , stellt Function<Int, out Any?> dar;

  • Function<, > , stellt Function<in Nothing, out Any?> dar.

Hinweis: Sternprojektionen sind sehr ähnlich zu den Java-ursprünglichen Typen (raw type), können aber sicher verwendet werden