Kotlin使用Class <T>参数调用java方法
我想要像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)
具有这个签名,并且从List
中获取这个错误“unresolved reference List” val ret = List<User>.javaClass
。
如果我使用像这样val ret = List<User>::class.java
我得到这个错误“只允许在类类型左边的类”
有什么问题? 以及这样做的正确方法是什么? 这是一个错误?
@ edwin的答案是正确的,你有一个XY问题 ,你实际上是在问错误的东西。 谢天谢地,你这样做的错误导致你在这里,@edwin能够解释做出正确行为的替代方案。
Java的每个绑定库都有一些像这样的模式( Class
与类型引用)。 所以这是学习和寻找你的图书馆,如RestTemplate,杰克逊,GSON和其他人的好东西。 实用程序类可以是ParameterizedTypeReference
中的一个, TypeReference
中的另一个, TypeRef
中的另一个等等; 但都做同样的事情。
现在,对于想知道原始代码有什么问题的人来说 ,要创建泛型擦除类引用(如Class<T>
,语法是:
val ref = List::class.java
您会注意到无法表示列表中项目的通用类型。 而且,即使可以,由于类型删除,它们也会被删除。 把这个传递给绑定库就像是在说“ 可能为null的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的生活比较容易:
在Kotlin中,编写扩展函数是很容易的,所以你不必多次执行这种类型的代码。 只需创建一个帮助器内联函数,该函数使用泛化的泛型来通过类型并创建ParameterizedTypeReference
:
inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}
现在你可以把@ edwin的例子改成:
val response = restTemplate.exchange(request, typeRef<List<String>>())
我认为你需要实际使用RestTemplate.exechange
方法,它的签名接受一个ParameterizedTypeReference<T>
,其中T
可以是你的响应的通用类型(在你的情况下是List<User>
)
假设我有一个终端返回JSON格式的Jedi名字列表,如下所示
["Obi-wan","Luke","Anakin"]
我想调用这个端点并得到一个List<String>
作为结果,列表中包含来自JSON的Jedi名称。
然后我可以这样做:
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
有一个List<String>
类型的参数。 这就是诀窍。
而当我尝试它时,这对我来说很有用。