Ответ 1
Ответ @edwin правильный, у вас была проблема XY, где вы действительно просили не того. К счастью, у вас была ошибка в этом, что привело вас сюда, и @edwin смог объяснить альтернативу, которая делает правильное поведение.
Каждая библиотека привязки для Java имеет такой же шаблон, что и тот же (Class
vs. type reference). Так что хорошо учиться и искать в своей библиотеке, таких как RestTemplate, Jackson, GSON и другие. Класс полезности может быть ParameterizedTypeReference
в одном, а TypeReference
в другом, TypeRef
в другом и так далее; но все делают то же самое.
Теперь, когда кто-то задается вопросом о том, что не так с исходным кодом, чтобы создать стандартную ссылку на классы, такие как Class<T>
, синтаксис будет выглядеть следующим образом:
val ref = List::class.java
Вы заметите, что нет способа выразить общий тип элементов в списке. И даже если бы вы могли, они все равно были бы стерты из-за стирания типа. Передача этого в библиотеку привязки будет напоминать "список, возможно, нулевой java.lang.Object", но хуже. Представьте Map<String, List<Int>>
, где теперь вы потеряли всю информацию, которая сделала бы десериализацию этого возможным.
Следующий не компилирует:
val ref = List<String>::class.java // <--- error only classes are allowed on left side
Сообщение об ошибке может быть более ясным, если "только классы без общих параметров разрешены в левой части::"
И ваше использование javaClass
может использоваться только на экземпляре, поэтому, если у вас есть список, удобный...
val ref = listOf("one", "two", "three").javaClass
Затем вы получите тип стираемого Class<T>
, который опять-таки неправильно используется здесь, но действительный синтаксис.
Итак, что показывает на самом деле код @edwin?
Создавая объект с супер-типом ParameterizedTypeReference
, этот код работает вокруг этой проблемы, потому что любой класс, даже если тип стирается, может видеть генерики в своем суперклассе и интерфейсах через линзу Type
. Например:
val xyz = object : MySuperClass<SomeGenerics>() {}
Этот экземпляр анонимного класса имеет суперкласс MySuperClass
с общими параметрами SomeGenerics
. Поэтому, если MySuperClass
содержит этот код:
abstract class MySuperClass<T> protected constructor() {
val type: Type = (javaClass.genericSuperclass as ParameterizedType)
.actualTypeArguments[0]
}
Затем вы можете добавить .type
в конец нашего объявления, чтобы получить доступ к этой функции:
val typeOfSomeGenerics = object : MySuperClass<SomeGenerics>() {}.type
И теперь у вас будет некоторая реализация Type
, описывающая наш класс SomeGenerics
. Он был бы одним из: Class
, ParameterizedType
, GenericArrayType
, TypeVariable
и WildcardType
И понимая и работая с ними, библиотека, которая выполняет привязку данных, знает достаточно, чтобы выполнить свою работу.
Жизнь в Котлине проще:
В Kotlin легко написать функции расширения, так что вам никогда не придется делать этот тип кода более одного раза. Просто создайте вспомогательную встроенную функцию, которая использует обобщенные генерические средства для прохождения через тип и создает ParameterizedTypeReference
:
inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}
И теперь вы можете изменить пример @edwin на:
val response = restTemplate.exchange(request, typeRef<List<String>>())