Метод вызова Kotlin java с аргументом Class <T>

Я хочу использовать Spring RestTemplate в Kotlin вот так:

//import org.springframework.web.client.RestTemplate
fun findAllUsers(): List<User> {
        val restTemplate = RestTemplate()

        //has error
        val ret = List<User>.javaClass
        return restTemplate.getForObject(URI(hostAddress), ret)
}

RestTemplate.getForObject(URI url, Class<T> responseType) имеет эту подпись, и я получаю эту ошибку "неразрешенный список ссылок" из List в val ret = List<User>.javaClass.

Если я так использую val ret = List<User>::class.java, я получаю эту ошибку: "Только классы разрешены в левой части литерала класса"

какая проблема? и какой правильный способ это сделать? Это ошибка?

Ответ 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>>())

Ответ 2

Я думаю, вам нужно использовать метод RestTemplate.exechange, у которого есть подпись, которая принимает ParameterizedTypeReference<T>, где T может быть типичным типом вашего ответа (в вашем случае a List<User>)

Предположим, что у меня есть конечная точка, которая возвращает список имен джедаев в формате JSON, как показано ниже.

["Obi-wan","Luke","Anakin"]

И я хотел бы вызвать эту конечную точку и получить List<String> как результат, со списком, содержащим имена Jedi из JSON.

Тогда я могу это сделать:

val endpoint = URI.create("http://127.0.0.1:8888/jedi.json")
val request = RequestEntity<Any>(HttpMethod.GET, endpoint)
val respType = object: ParameterizedTypeReference<List<String>>(){}
val response = restTemplate.exchange(request, respType)
val items: List<String> = response.body;
println(items) //prints [Obi-wan, Luke, Anakin]

Обратите внимание: my ParameterizedTypeReference имеет аргумент типа List<String>. Это трюк.

И это сработало для меня, когда я попробовал.

Ответ 3

Вы можете попробовать

return restTemplate.getForObject(URI(hostAddress), Array<User>::class.java).toList()