我想在Kotlin中使用Spring RestTemplate
,如下所示:
//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)
有此签名,我收到此错误&#34;未解析的参考列表&#34;来自List
中的val ret = List<User>.javaClass
。
如果我这样使用val ret = List<User>::class.java
我会收到此错误&#34;只允许在类文字的左侧使用类&#34;
问题是什么?这样做的正确方法是什么?这是一个错误吗?
答案 0 :(得分:36)
@ edwin的回答是正确的,你有一个XY Problem,你实际上是在猜错。值得庆幸的是,你这样做有一个错误导致你在这里,@ edwin能够解释做出正确行为的替代方案。
每个Java绑定库都有一些相同的模式(Class
与类型引用相同)。因此,在您的图书馆中学习和寻找是好事,例如RestTemplate,Jackson,GSON等。实用程序类可以在一个ParameterizedTypeReference
中,在另一个中TypeReference
,在另一个中TypeRef
,依此类推;但都在做同样的事情。
现在,对于想知道原始代码有什么问题的人,要创建一个泛型擦除类引用,例如Class<T>
,语法将为:
val ref = List::class.java
您会注意到无法表达列表中项目的泛型类型。即使你可以,它们也会因类型擦除而被删除。将它传递给绑定库就像是说&#34; 一个可能为空的java.lang.Object的列表&#34;但更糟糕的是想象一下Map<String, List<Int>>
你现在丢失了所有可能使反序列化成为可能的信息。
以下不编译:
val ref = List<String>::class.java // <--- error only classes are allowed on left side
错误信息可以更明确地说&#34; 只允许没有通用参数的类在:: &#34;
的左侧你对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的生活更轻松:
在Kotlin中,编写扩展函数很容易,因此您不必多次执行此类代码。只需创建一个帮助器内联函数,该函数使用reified泛型来传递类型并创建ParameterizedTypeReference
:
inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}
现在你可以将@ edwin的例子改为:
val response = restTemplate.exchange(request, typeRef<List<String>>())
答案 1 :(得分:11)
我认为您需要实际使用具有接受RestTemplate.exechange
签名的ParameterizedTypeReference<T>
方法,其中T
可以是您的响应的通用类型(在您的情况下为{{ 1}})
假设我有一个以JSON格式返回Jedi名称列表的端点,如下所示
List<User>
我想调用此端点并获得["Obi-wan","Luke","Anakin"]
作为结果,列表中包含JSON中的Jedi名称。
然后我可以这样做:
List<String>
请注意,我的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]
的类型参数为ParameterizedTypeReference
。这就是诀窍。
当我尝试它时,这对我有用。
答案 2 :(得分:7)
您可以尝试
return restTemplate.getForObject(URI(hostAddress), Array<User>::class.java).toList()
答案 3 :(得分:0)
首先,您将创建一个新类-UserList.kt(名称是我们的所有者)
内部:
class UserList : MutableList<User> by ArrayList()
之后,您可以调用具有RestTemplate
的方法
val response: List < Item > ? = restTemplate.getObject("yourEndpoint", UserList::class.java)
可能是返回值null
,所以您需要注意这一点。