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>
Sostituire ** Qualsiasi ** con l'interfaccia desiderata, e lavorare con l'interfaccia :) –