reified în Kotlin

Generics Kotlin 1.0

reified păstrează tipul generic T la rulare într-o funcție inline, ca să poți scrie x is T sau T::class.

Sintaxă
inline fun <reified T> numeFunctie(...) {
    if (x is T) ...
    T::class.simpleName
}

Ce face reified

Pe JVM, tipurile generice se șterg la compilare (type erasure): într-o funcție obișnuită fun <T> f() nu poți afla la rulare ce este T, deci x is T nu compilează. Dacă funcția este inline și parametrul de tip este marcat reified, compilatorul copiază funcția la fiecare apel și pune acolo tipul real. Atunci is T, as T și T::class funcționează.

Biblioteca standard folosește reified în filterIsInstance<T>(), emptyArray<T>() sau typeOf<T>(). O funcție reified poate fi apelată doar cu tipuri cunoscute la compilare, nu cu un alt T ne-reified.

Exemple

Exemplul 1: Filtrare după tip

doar<String>() verifică fiecare element cu is T; fără reified verificarea nu ar fi posibilă.

inline fun <reified T> List<Any>.doar(): List<T> {
    val rezultat = mutableListOf<T>()
    for (x in this) if (x is T) rezultat.add(x)
    return rezultat
}

fun main() {
    val amestec: List<Any> = listOf("Ana", 10, "Iași", 3.5, 7, true)
    println(amestec.doar<String>())
    println(amestec.doar<Int>())
    println(amestec.doar<Boolean>())
}

Rezultat

[Ana, Iași]
[10, 7]
[true]

Exemplul 2: Numele tipului cu T::class

T::class.simpleName dă numele clasei reale de la fiecare apel. Pentru List<Int> se vede doar List: tipul elementelor se șterge.

class Produs
class Comanda

inline fun <reified T> descrie(): String = "Lucrez cu tipul ${T::class.simpleName}"

fun main() {
    println(descrie<Produs>())
    println(descrie<Comanda>())
    println(descrie<String>())
    println(descrie<List<Int>>())
}

Rezultat

Lucrez cu tipul Produs
Lucrez cu tipul Comanda
Lucrez cu tipul String
Lucrez cu tipul List

Exemplul 3: Conversie sigură cu as? T

valoare<T> citește o setare din map și o returnează doar dacă are tipul cerut; altfel dă valoarea implicită.

val setari: Map<String, Any> = mapOf("oras" to "Cluj", "port" to 8080, "debug" to false)

inline fun <reified T> valoare(cheie: String, implicit: T): T = setari[cheie] as? T ?: implicit

fun main() {
    val oras = valoare("oras", "necunoscut")
    val port = valoare("port", 80)
    val gresit = valoare("oras", 0)
    println("Oraș: $oras, port: $port, oraș ca număr: $gresit")
}

Rezultat

Oraș: Cluj, port: 8080, oraș ca număr: 0

Greșeli frecvente

  • reified într-o funcție care nu este inline: eroare only type parameters of inline functions can be reified.
  • Verificarea tipului complet al unei colecții: x is List<String> verifică doar că este o listă; tipul elementelor se șterge și acolo. T::class pentru List<Int> dă List.
  • Apelarea din altă funcție generică fără reified: fun <T> g() = doar<T>() nu compilează, pentru că T nu mai este cunoscut.

Comenzi înrudite

  • inline, noinline și crossinline: inline copiază corpul funcției și lambda-urile ei în locul apelului; noinline și crossinline controlează lambda-urile primite.
  • Generics (<T>) și where: Generics permit clase și funcții care lucrează cu orice tip (T), verificat la compilare; where adaugă mai multe restricții pe T.
  • is, !is și smart cast: is verifică tipul unei valori; după verificare, compilatorul o tratează automat ca acel tip (smart cast).
  • as și as? (conversie de tip): as tratează o valoare ca alt tip (aruncă excepție dacă nu se potrivește); as? întoarce null în loc de excepție.
  • Iterable.filterIsInstance: Păstrează doar elementele de un anumit tip și întoarce o listă cu acel tip.