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

moj*_*b23 17 java kotlin

我想RestTemplate在Kotlin中使用Spring ,如下所示:

//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)
}
Run Code Online (Sandbox Code Playgroud)

RestTemplate.getForObject(URI url, Class<T> responseType)有这个签名,我从得到这个错误"未解决的参考序列" Listval ret = List<User>.javaClass.

如果我这样使用,val ret = List<User>::class.java我会收到此错误"只允许类在类文字的左侧"

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

Jay*_*ard 40

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

Java的每个绑定库都有一些相同的模式(Class与类型引用相对).因此,在您的图书馆中学习和寻找是好事,例如RestTemplate,Jackson,GSON等.实用类可以ParameterizedTypeReference在一个中,TypeReference在另一个中,TypeRef在另一个中,依此类推; 但都在做同样的事情.

现在,对于想知道原始代码有什么问题的人来说,创建一个泛型擦除类引用,如Class<T>语法将是:

val ref = List::class.java
Run Code Online (Sandbox Code Playgroud)

您会注意到无法表达列表中项目的泛型类型.即使你可以,它们也会因类型擦除而被删除.将其传递给绑定库就像是说" 可能是可以为空的java.lang.Object的列表 ",但更糟糕的是.想象一下Map<String, List<Int>>,您现在丢失了所有可能使反序列化成为可能的信息.

以下内容无法编译:

val ref = List<String>::class.java  // <--- error only classes are allowed on left side
Run Code Online (Sandbox Code Playgroud)

错误消息可以更清楚地说" 在::的左侧只允许没有通用参数的类 "

而你的使用javaClass只能在一个实例上使用,所以如果你碰巧有一个方便的列表......

val ref = listOf("one", "two", "three").javaClass  
Run Code Online (Sandbox Code Playgroud)

然后你会得到一个类型擦除Class<T>,这在这里使用是错误的,但有效的语法.

那么@edwin实际显示的代码是什么呢?

通过创建一个具有超类型ParameterizedTypeReference的对象,代码解决了这个问题,因为任何类,即使类型被擦除,也可以通过镜头看到其超类和接口中的泛型Type.例如:

val xyz = object : MySuperClass<SomeGenerics>() {}
Run Code Online (Sandbox Code Playgroud)

这个匿名类的实例MySuperClass具有通用参数的超类SomeGenerics.所以如果MySuperClass包含这个代码:

abstract class MySuperClass<T> protected constructor() {
    val type: Type = (javaClass.genericSuperclass as ParameterizedType)
                                 .actualTypeArguments[0]
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以添加 .type到声明的末尾以访问此功能:

val typeOfSomeGenerics = object : MySuperClass<SomeGenerics>() {}.type
Run Code Online (Sandbox Code Playgroud)

现在你将有一些Type描述我们SomeGenerics班级的实现.这将是一个: Class,ParameterizedType,GenericArrayType,TypeVariable,和WildcardType

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

Kotlin的生活更轻松:

在Kotlin中,编写扩展函数很容易,因此您不必多次执行此类代码.只需创建一个帮助器内联函数,该函数使用reified泛型来传递类型并创建ParameterizedTypeReference:

inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}
Run Code Online (Sandbox Code Playgroud)

现在您可以将@ edwin的示例更改为:

val response = restTemplate.exchange(request, typeRef<List<String>>())
Run Code Online (Sandbox Code Playgroud)

  • 简而言之。创建一个类`open class ParameterizedTypeReference &lt;T&gt;`。并这样调用函数:`myFunc((object:ParameterizedTypeReference &lt;List &lt;Long &gt;&gt;(){}。javaClass.genericSuperclass as ParameterizedType).actualTypeArguments [0])` (2认同)

Edw*_*rzo 12

我认为你需要实际使用的RestTemplate.exechange有一个接受的签名方法ParameterizedTypeReference<T>,其中T可以是一般类型的反应(在你的情况下List<User>)

假设我有一个端点,以JSON格式返回Jedi名称列表,如下所示

["Obi-wan","Luke","Anakin"]
Run Code Online (Sandbox Code Playgroud)

我想调用这个端点并获得一个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]
Run Code Online (Sandbox Code Playgroud)

请注意,我ParameterizedTypeReference的类型参数为List<String>.这就是诀窍.

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

  • `RestTemplate.getForObject`需要一个`Class <T>`作为参数来识别你的响应类型,但是一个类不足以解释你想要什么.例如,您希望将`List.class`作为响应,但它不会告诉列表是哪个类型参数(例如`List <User>`).因此,您需要一个不同的方法签名,它使用`ParameterizedTypeReference`来告诉事物,结果类的类型及其类型参数. (3认同)

jcr*_*ada 8

你可以试试

return restTemplate.getForObject(URI(hostAddress), Array<User>::class.java).toList()
Run Code Online (Sandbox Code Playgroud)

  • 这是解决此问题的最优雅,最流畅的方法。 (2认同)