English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
La genericidad, es decir, "tipos parametrizados", parametriza los tipos, que se pueden usar en clases, interfaces y métodos.
Como Java, Kotlin también ofrece genericidad, proporcionando una garantía de seguridad de tipo y eliminando las molestias de la conversión de tipo forzado.
Se declara una clase genérica:
class Box<T>(t: T) { var value = t }
Al crear un ejemplo de clase, necesitamos especificar los parámetros de tipo:
val box: Box<Int> = Box<Int>(1) // o val box = Box(1) // El compilador realiza la inferencia de tipo,1 El tipo es Int, por lo que el compilador sabe que estamos hablando de Box<Int>.
El siguiente ejemplo muestra cómo pasar datos de tipo entero y cadena a la clase genérica 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) }
Le résultat de la sortie est :
10 w3codebox
Se puede definir una variable de tipo genérico, se puede escribir completamente el parámetro de tipo, o se puede omitir el parámetro de tipo si el compilador puede inferirlo automáticamente.
La declaración de funciones genéricas de Kotlin es igual que la de Java, los parámetros de tipo deben colocarse delante del nombre de la función:
fun <T> boxIn(value: T) = Box(value) // Los siguientes son declaraciones válidas val box4 = boxIn<Int>(1) val box5 = boxIn(1) // El compilador realiza la inferencia de tipo
Al llamar a una función genérica, si se puede inferir el parámetro de tipo, se puede omitir el parámetro genérico.
Le siguiente ejemplo crea una función genérica doPrintln, que realiza el tratamiento correspondiente según el tipo de entrada:
fun main(args: Array<String>) { val age = 23 val name = "w3codebox" val bool = true doPrintln(age) // Entier doPrintln(name) // Chaîne de caractères doPrintln(bool) // Booleen } fun <T> doPrintln(content: T) { when (content) { is Int -> println("Le nombre entier est $content") is String -> println("Convertir une chaîne en majuscules : ${content.toUpperCase()}") else -> println("T n'est pas un entier, ni une chaîne de caractères") } }
Le résultat de la sortie est :
Le nombre entier est 23 Convertir une chaîne en majuscules : w3codebox T n'est pas un entier, ni une chaîne de caractères
Nous pouvons utiliser des contraintes génériques pour définir les types autorisés pour un paramètre donné.
Kotlin utilise : pour contraindre les types génériques à leurs bornes supérieures.
La contrainte la plus courante est la borne supérieure (upper bound) :
fun <T : Comparable<T>> sort(list: List<T>) { // …… }
Les sous-types de Comparable peuvent remplacer T. Par exemple:
sort(listOf(1, 2, 3)) // OK. Int est un sous-type de Comparable<Int> sort(listOf(HashMap<Int, String>())) // Erreur : HashMap<Int, String> n'est pas un sous-type de Comparable<HashMap<Int, String>>
La borne supérieure par défaut est Any?.
Pour plusieurs conditions de bornes supérieures, vous pouvez utiliser la clause where :
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String> where T : CharSequence, T : Comparable<T> { return list.filter { it > threshold }.map { it.toString() } }
Kotlin n'a pas de types génériques génériques, il en a deux autres : la variance de déclaration (declaration-site variance) et les projections de type (type projections).
Le type variance à la déclaration est modifié par le modificateur d'annotation covariant : in, out, consommateur in, producteur out.
Utilisez out pour rendre un paramètre de type covariant, les types covariants peuvent ne pas être utilisés comme types d'entrée, mais peuvent être utilisés comme types de retour :
// Définir une classe supportant la covariance 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()) // Sortie a }
in rend un paramètre de type contravariant, le paramètre de type contravariant ne peut être utilisé que comme entrée, il peut être utilisé comme type d'entrée mais ne peut pas être utilisé comme type de retour:
// Définir une classe qui prend en charge l'inverse de la covariété 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 }
Parfois, vous pourriez vouloir indiquer que vous ne savez rien des informations sur les paramètres de type, mais souhaitez tout de même pouvoir l'utiliser de manière sécurisée. Par 'utilisation sécurisée', on entend définir une projection de type pour un type générique, qui nécessite que toutes les instances du type générique soient des sous-types de cette projection.
Pour ce problème, Kotlin propose une syntaxe appelée projection par étoile (star-projection):
Si le type est défini comme Foo<out T> , où T est un paramètre de type covariant, la limite supérieure (upper bound) est TUpper , Foo<Équivaut à Foo<out TUpper> . Cela signifie que lorsque T est inconnu, vous pouvez utiliser de manière sécurisée Foo<Lit des valeurs du type TUpper .
Si le type est défini comme Foo<in T> , où T est un paramètre de type contravariant, Foo<Équivaut à Foo<inNothing> . Cela signifie que lorsque T est inconnu, vous ne pouvez pas utiliser de manière sécurisée Foo<Écriture de tout.
Si le type est défini comme Foo<T> , où T est un paramètre de type covariant, la limite supérieure (upper bound) est TUpper , pour les cas de lecture de valeurs, Foo<*Équivaut à Foo<out TUpper> , pour les cas d'écriture de valeurs, équivaut à Foo<in Nothing> .
Si un type générique contient plusieurs paramètres de type, chaque paramètre de type peut être投射. Par exemple, si le type est défini comme interface Function<in T, out U> , les types suivants peuvent apparaître:
Function<*, String> , représente Function<in Nothing, String> ;
Function<Int, *> , représente Function<Int, out Any?> ;
Function<, > , représente Function<in Nothing, out Any?> .
Attention : les projections étoilées sont très similaires aux types natifs (raw type) de Java, mais peuvent être utilisées en toute sécurité