¿Cómo get los arguments de tipo reales de un parámetro genérico reificado en Kotlin?

Con los parameters de tipo reificado , se puede escribir una function en línea que funcione con el parámetro tipo a través de la reflexión en time de ejecución:

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

Pero cuando se llama a f con un parámetro que es en sí mismo una class genérica, parece que no hay forma de get sus arguments de tipo reales a través de T::class :

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

¿Hay alguna manera de get arguments de tipo real de un genérico reificado a través de la reflexión?

Debido al borrado de tipo , los arguments generics reales no se pueden get a través del token T::class de una class genérica. Los diferentes objects de una class deben tener el mismo token de class, por eso no puede contener arguments generics reales.

Pero hay un techinque llamado super tipo tokens que puede dar arguments de tipo reales en caso de que el tipo se conozca en time de compilation (es cierto para los generics reificados en Kotlin debido a la inclusión).

El truco es que el comstackdor retiene los arguments de tipo reales para una class no genérica derivada de una class genérica (todas sus instancias tendrán los mismos arguments, buena explicación aquí ). Se puede acceder a través de clazz.genericSuperClass.actualTypeArguments de una instancia Class<*> .

Dado todo eso, puedes escribir una class de utilidad como esta:

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

Explicado en Jackson TypeReference que utiliza el mismo enfoque. El module Jackson Kotlin lo usa en generics reificados.

Después de eso, en una function en línea con un genérico reificado, TypeReference necesita ser subclasificado (una expresión de object irá), y luego se puede usar su type .

Ejemplo:

 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>