Ответ 1
Из-за стирания типа фактические общие аргументы не могут быть получены с помощью токена T::class
универсального класса. Разные объекты класса должны иметь один и тот же токен класса, поэтому он не может содержать фактические общие аргументы.
Но есть технология, называемая токены супертипа, которая может давать фактические аргументы типа в случае, когда тип известен во время компиляции (это верно для обобщенных обобщений в Kotlin из-за встраивания).
Редактировать: Начиная с Kotlin 1.3.50, в соответствии с методикой, описанной ниже, для получения информации о типе для параметра типа reified больше нет необходимости. Вместо этого вы можете использовать typeOf<T>()
для параметров типа reified.
Хитрость заключается в том, что компилятор сохраняет фактические аргументы типа для неуниверсального класса, производного от универсального класса (все его экземпляры будут иметь одинаковые аргументы, хорошее объяснение здесь). Они доступны через clazz.genericSuperClass.actualTypeArguments
экземпляра Class<*>
.
Учитывая все это, вы можете написать класс утилит следующим образом:
abstract class TypeReference<T> : Comparable<TypeReference<T>> {
val type: Type =
(javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
override fun compareTo(other: TypeReference<T>) = 0
}
Explained in Jackson TypeReference which uses the same approach. Jackson Kotlin module uses it on reified generics.
После этого в встроенной функции с улучшенным обобщенным значением TypeReference
необходимо разделить на подклассы (будет использовано объектное выражение), а затем можно использовать его type
.
Пример:
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>