繁体   English   中英

Kotlin用Class <T>参数调用java方法

[英]Kotlin call java method with Class<T> argument

我想在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)具有此签名,我从val ret = List<User>.javaClass List中获取此错误“未解析的引用列表”。

如果我像这样使用val ret = List<User>::class.java我得到这个错误“只允许类在类文字的左侧”

有什么问题? 这样做的正确方法是什么? 这是一个错误吗?

@ edwin的回答是正确的,你有一个XY问题 ,你实际上是在问错误。 值得庆幸的是,你这样做有一个错误导致你在这里,@ edwin能够解释做出正确行为的替代方案。

Java的每个绑定库都有一些相同的模式( Class与类型引用)。 因此,在您的图书馆中学习和寻找是好事,例如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之一: ClassParameterizedTypeGenericArrayTypeTypeVariableWildcardType

通过理解和使用它们,一个执行数据绑定的库知道足以完成它的工作。

Kotlin的生活更轻松:

在Kotlin中,编写扩展函数很容易,因此您不必多次执行此类代码。 只需创建一个帮助器内联函数,该函数使用reified泛型来传递类型并创建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> 这就是诀窍。

当我尝试它时,这对我有用。

你可以试试

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

首先,您将创建一个新类 - UserList.kt(该名称是我们的所有者)

内:

class UserList : MutableList<User> by ArrayList()

之后,您可以调用具有RestTemplate方法

val response: List < Item > ? = restTemplate.getObject("yourEndpoint", UserList::class.java)

它可能返回null ,因此您需要处理它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM