【问题标题】:Kotlin call java method with Class<T> argumentKotlin 使用 Class<T> 参数调用 java 方法
【发布时间】:2016-09-24 17:40:29
【问题描述】:

我想像这样在 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&lt;T&gt; responseType) 有这个签名,我从List in val ret = List&lt;User&gt;.javaClass 得到这个错误“未解析的引用列表”。

如果我像这样使用val ret = List&lt;User&gt;::class.java,我会收到此错误“类文字的左侧只允许使用类”

有什么问题?这样做的正确方法是什么?是bug吗?

【问题讨论】:

标签: java kotlin


【解决方案1】:

@edwin 的回答是正确的,你有一个XY Problem,你实际上是在要求错误的东西。值得庆幸的是,您在执行此操作时遇到了错误,导致您来到这里,@edwin 能够解释执行正确行为的替代方案。

Java 的每个绑定库都有一些类似的相同模式(Class 与类型引用)。所以这是在你的库中学习和寻找的好东西,比如 RestTemplate、Jackson、GSON 等。实用程序类可能是 ParameterizedTypeReference 在一个,TypeReference 在另一个,TypeRef 在另一个等等;但所有人都在做同样的事情。

现在,对于任何想知道原始代码有什么问题的人,要创建一个泛型擦除类引用,例如Class&lt;T&gt;,语法如下:

val ref = List::class.java

您会注意到无法表示列表中项目的通用类型。即使可以,由于类型擦除,它们无论如何都会被擦除。将此传递给绑定库就像说“请列出可能为空的 java.lang.Object 的列表”,但更糟。想象一下Map&lt;String, List&lt;Int&gt;&gt;,您现在丢失了所有使反序列化成为可能的信息。

以下无法编译:

val ref = List<String>::class.java  // <--- error only classes are allowed on left side

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

而您对javaClass 的使用只能在一个实例上使用,所以如果您手头有一个列表...

val ref = listOf("one", "two", "three").javaClass  

那么你最终会得到一个类型被删除的Class&lt;T&gt;,这也是在这里使用的错误,但语法有效。

那么@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 类。它将是以下之一:ClassParameterizedTypeGenericArrayTypeTypeVariableWildcardType

通过理解和使用这些,进行数据绑定的库知道足以完成其工作。

在 Kotlin 中生活更轻松:

在 Kotlin 中,编写扩展函数很容易,这样您就不必多次编写此类代码。只需创建一个辅助内联函数,该函数使用具体化的泛型来传递类型并创建ParameterizedTypeReference

inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}

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

val response = restTemplate.exchange(request, typeRef<List<String>>())

【讨论】:

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

你可以试试

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

【讨论】:

  • 这是这个问题最优雅流畅的解决方案。
【解决方案3】:

我认为您需要实际使用RestTemplate.exechange 方法,该方法的签名接受ParameterizedTypeReference&lt;T&gt;,其中T 可以是您响应的通用类型(在您的情况下为List&lt;User&gt;

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

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

我想调用这个端点并得到一个List&lt;String&gt; 作为结果,列表中包含来自 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]

请注意,我的 ParameterizedTypeReference 的类型参数为 List&lt;String&gt;。这就是诀窍。

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

【讨论】:

  • RestTemplate.getForObject 需要一个Class&lt;T&gt; 作为参数来标识您的响应类型,但是一个类不足以解释您想要什么。例如,您希望得到List.class 作为响应,但它没有说明该列表是哪个类型的参数(例如List&lt;User&gt;)。因此,您需要一个不同的方法签名,它使用ParameterizedTypeReference 来告诉这两件事,结果类的类型及其类型参数。
【解决方案4】:

首先,你将创建一个新类——UserList.kt(名字是我们的所有者)

内部:

class UserList : MutableList<User> by ArrayList()

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

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

可能是返回null,所以你需要注意它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-28
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    • 2013-10-06
    • 1970-01-01
    相关资源
    最近更新 更多