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

Generici in Kotlin

I generics, o "tipi parametrizzati", parametrizzano i tipi, possono essere utilizzati in classi, interfacce e metodi.

Come Java, Kotlin fornisce anche generics, che garantiscono la sicurezza del tipo e eliminano il fastidio delle conversioni di tipo.

Declarare una classe generica:

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

Quando si crea un esempio di classe, è necessario specificare i parametri di tipo:

val box: Box<Int> = Box<Int>(1)
// O
val box = Box(1) // Il compilatore fa l'inferenza del tipo, 1 è di tipo Int, quindi il compilatore sa che stiamo parlando di Box<Int>.

Esempio di utilizzo di dati di tipo intero e stringa nella classe generica 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)
}

Il risultato dell'output è:

10
w3codebox

Definire una variabile di tipo generico, è possibile scrivere chiaramente i parametri di tipo, se il compilatore può determinare automaticamente i parametri di tipo, possono essere omessi i parametri di tipo.

La dichiarazione di funzione generica di Kotlin è la stessa di Java, i parametri di tipo devono essere messi davanti al nome della funzione:

fun <T> boxIn(value: T) = Box(value)
// Seguono espressioni legali
val box4 = boxIn<Int>(1)
val box5 = boxIn(1) // Il compilatore fa l'inferenza del tipo

Quando si chiama una funzione generica, se è possibile inferire il parametro di tipo, è possibile omettere il parametro generico.

Esempio di creazione di una funzione generica doPrintln, che gestisce diversi tipi di input:

fun main(args: Array<String>) {
    val age = 23
    val name = "w3codebox"
    val bool = true
    doPrintln(age) // Intero
    doPrintln(name) // Stringa
    doPrintln(bool) // Booleano
}
fun <T> doPrintln(content: T) {
    when (content) {
        is Int -> println("Numero intero: $content")
        is String -> println("Conversione di stringa in maiuscolo: ${content.toUpperCase()}")
        else -> println("T non è un intero né una stringa")
    }
}

Il risultato dell'output è:

Il numero intero è 23
Converti la stringa in maiuscolo: w3codebox
T non è un intero né una stringa

Vincoli generici

Possiamo usare vincoli generici per stabilire i tipi permessi per un determinato parametro.

In Kotlin si utilizza : per vincolare il tipo superiore dei generici.

La restrizione più comune è il limite superiore (upper bound):

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

I sotto tipi di Comparable possono sostituire T. Ad esempio:

sort(listOf(1, 2, 3)) // OK. Int è un sotto tipo di Comparable<Int>
sort(listOf(HashMap<Int, String>())) // Errore: HashMap<Int, String> non è un sotto tipo di Comparable<HashMap<Int, String>>

Il limite superiore predefinito è Any?.

Per più vincoli superiori, è possibile usare la clausola 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() }
}

Variazione

In Kotlin non ci sono tipi di jolly, ci sono invece due altre cose: la variazione dichiarativa (declaration-site variance) e le proiezioni di tipo (type projections).

Variazione dichiarativa

La variazione di tipo dichiarativa utilizza l'annotazione di covarianza: in, out, consumatore in, produttore out.

Usare out per rendere un tipo parametrico covariante, i tipi parametrici covarianti possono essere usati solo come tipo di ritorno, ma non come tipo di input:

// Definire una classe che supporta la covarianza
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())   // Output: a
}

in rende un parametro di tipo contravariante, i parametri di tipo contravariante possono essere usati solo come input, possono essere usati come tipo di input ma non come tipo di ritorno:

// Definisci una classe che supporta la contravarianza
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
}

Proiezione con stella

A volte, potresti voler rappresentare che non conosci alcuna informazione sui parametri di tipo, ma desideri comunque usarli in modo sicuro. Per "usare in modo sicuro" si intende definire una proiezione di tipo per un tipo generico, che richiede che tutti gli esempi delle entità di questo tipo generico siano sottotipi di questa proiezione.

Per questo problema, Kotlin fornisce una sintassi chiamata proiezione con stella (star-projection):

  • Supponiamo che il tipo sia definito come Foo<out T>, dove T è un parametro di tipo covariante, con il limite superiore (upper bound) TUpper, Foo<> È equivalente a Foo<out TUpper>. Questo significa che quando T è sconosciuto, puoi usare in modo sicuro Foo<> Leggi il valore del tipo TUpper.

  • Supponiamo che il tipo sia definito come Foo<in T>, dove T è un parametro di tipo contravariante, Foo<> È equivalente a Foo<inNothing>. Questo significa che quando T è sconosciuto, non puoi usare in modo sicuro Foo<> Scrivi qualsiasi cosa.

  • Supponiamo che il tipo sia definito come Foo<T>, dove T è un parametro di tipo covariante, con il limite superiore (upper bound) TUpper, per le occasioni di lettura dei valori, Foo<*> è equivalente a Foo<out TUpper>, per le occasioni di scrittura dei valori, è equivalente a Foo<in Nothing>.

Se un tipo generico contiene più parametri di tipo, ogni parametro di tipo può essere proiettato separatamente. Ad esempio, se il tipo è definito come interface Function<in T, out U>, possono apparire le seguenti proiezioni con stella:

  • Function<*, String> , rappresenta Function<in Nothing, String> ;

  • Function<Int, *> , rappresenta Function<Int, out Any?> ;

  • Function<, >, rappresenta Function<in Nothing, out Any?> .

Attenzione: La proiezione di stella è molto simile ai tipi nativi di Java, ma può essere utilizzata in modo sicuro