2016-03-27 8 views
14

Utilizzando reified type parameters, si può scrivere una funzione inline che funziona con il parametro type attraverso la riflessione a runtime:Come ottenere argomenti di tipo reali di un parametro generico reificato in Kotlin?

inline fun <reified T: Any> f() { 
    val clazz = T::class 
    // ... 
} 

Ma quando f è chiamato con un parametro che è esso stesso una classe generica, non sembra esserci alcun modo per ottenere i suoi argomenti di tipo reale attraverso T::class:

f<List<Integer>>() // T::class is just kotlin.collections.List 

c'è un modo per ottenere gli argomenti tipo effettivo di un generico reificato attraverso la riflessione?

+0

Sostituire ** Qualsiasi ** con l'interfaccia desiderata, e lavorare con l'interfaccia :) –

risposta

22

A causa di type erasure, non è possibile ottenere argomenti generici effettivi tramite il token T::class di una classe generica. Oggetti diversi di una classe devono avere lo stesso token di classe, ecco perché non può contenere argomenti generici reali.

Ma esiste una tecnica chiamata super type tokens che può fornire argomenti di tipo effettivo nel caso in cui il tipo sia noto al momento della compilazione (è vero per i generici reificati in Kotlin a causa dell'inlining).

Il trucco è che il compilatore conserva gli argomenti di tipo effettivo per una classe non generica derivata da una classe generica (tutte le sue istanze avranno gli stessi argomenti, buona spiegazione here). Sono accessibili tramite clazz.genericSuperClass.actualTypeArguments di un'istanza Class<*>.

Considerato tutto questo, è possibile scrivere una classe util come questo:

abstract class TypeReference<T> : Comparable<TypeReference<T>> { 
    val type: Type = 
     (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] 

    override fun compareTo(other: TypeReference<T>) = 0 
} 

spiegata in Jackson TypeReference che utilizza lo stesso approccio. Modulo Jackson Kotlin uses it su generici reificati.

Dopo di che, in una funzione inline con reificate generici TypeReference esigenze da sottoclasse (a object expression andrà), e quindi la sua può essere utilizzato type.

Esempio:

inline fun <reified T: Any> printGenerics() { 
    val type = object : TypeReference<T>() {}.type 
    if (type is ParameterizedType) 
     type.actualTypeArguments.forEach { println(it.typeName) } 
} 

printGenerics<HashMap<Int, List<String>>>():

java.lang.Integer 
java.util.List<? extends java.lang.String> 
Problemi correlati