Отражение Котлина: неизвестный тип параметра
Я провожу несколько экспериментов с отражением Котлина.
Я пытаюсь получить объект отражения универсального класса с его аргументом.
На Java это было бы ParameterizedType
,
Способ получить такую вещь с помощью API отражения Java немного запутан: создайте анонимный подкласс обобщенного класса, а затем получите его первый параметр супертипа.
Вот пример:
@Suppress("unused") @PublishedApi
internal abstract class TypeReference<T> {}
inline fun <reified T> jGeneric() =
((object : TypeReference<T>() {}).javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
Когда я println(jGeneric<List<String?>>())
печатает java.util.List<? extends java.lang.String>
что логично, учитывая, что Котлина List
использует сайт объявлений out
Дисперсия и что типы Java не имеют понятия обнуляемости.
Теперь я хотел бы добиться такого же результата, но с API отражения Kotlin (который, конечно, содержал бы информацию об обнуляемости).
Конечно, List<String>::class
не может работать, так как это дает KClass
, и я ищу KType
,
Тем не менее, когда я пытаюсь это:
inline fun <reified T> kGeneric() =
(object : TypeReference<T>() {})::class.supertypes[0].arguments[0].type
Когда я println(kGeneric<List<String?>>())
печатает [ERROR : Unknown type parameter 0]
что вполне... ну, антиклиматично;)
Как я могу получить в Котлине KType
отражающий List<String>
?
1 ответ
Чтобы создать KType
Например, в Kotlin 1.1 у вас есть два варианта:
Чтобы создать простой ненулевой тип из
KClass
где класс либо не является универсальным, либо вы можете заменить все его параметры типа на звездные проекции (*
), использоватьstarProjectedType
имущество. Например, следующее создаетKType
представляющий ненулевой типString
:val nonNullStringType = String::class.starProjectedType
Или следующее создает
KType
представляющий ненулевой типList<*>
:val nonNullListOfSmth = List::class.starProjectedType
Для более сложных случаев используйте
createType
функция. Он принимает класс, аргументы типа и должен ли тип быть обнуляемым. Аргументы типа представляют собой списокKTypeProjection
который просто тип + дисперсия (вход / выход / нет). Например, следующий код создаетKType
экземпляр, представляющийList<String>
:val nonNullStringType = String::class.starProjectedType val projection = KTypeProjection.invariant(nonNullStringType) val listOfStrings = listClass.createType(listOf(projection))
Или следующее создает тип
List<String>?
:val listOfStrings = listClass.createType(listOf(projection), nullable = true)
И то и другое starProjectedType
а также createType
определены в упаковке kotlin.reflect.full
,
Мы планируем ввести возможность получения KType
например, просто из параметра reified типа встроенной функции, которая может помочь в некоторых случаях, когда необходимый тип известен статически, однако в настоящее время не совсем ясно, возможно ли это без значительных накладных расходов. Итак, пока это не реализовано, пожалуйста, используйте декларации, описанные выше.