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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39679180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 04:33:32  来源:igfitidea点击:

Kotlin call java method with Class<T> argument

javakotlin

提问by mojtab23

I want to use Spring RestTemplatein Kotlin like this:

我想像这样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)
}

The RestTemplate.getForObject(URI url, Class<T> responseType)has this signature and I get this error "unresolved reference List" from Listin val ret = List<User>.javaClass.

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

If I use like this val ret = List<User>::class.javaI get this error "Only classes are allowed on the left hand side of a class literal"

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

what's the problem? and what's the proper way for doing this? Is it a bug?

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

回答by Jayson Minard

@edwin's answer is correct, you had an XY Problemwhere you were actually asking for the wrong thing. Thankfully you had an error in doing so which led you here and @edwin was able to explain the alternative which does the correct behavior.

@edwin 的回答是正确的,您遇到了XY 问题,您实际上要求错了。值得庆幸的是,您在这样做时出错了,导致您来到这里,@edwin 能够解释执行正确行为的替代方案。

Every binding library for Java has some pattern like this that is the same (Classvs. type reference). So this is good thing to learn and look for in your library such as RestTemplate, Hymanson, GSON and others. The utility class maybe is ParameterizedTypeReferencein one, and TypeReferencein another, TypeRefin yet another and so on; but all doing the same thing.

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

Now, for anyone wondering what was wrong with the original code, to create an generics erased class reference such as Class<T>the syntax would be:

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

val ref = List::class.java

You will notice there is no way to express the generic type of the items in the list. And even if you could, they would be erased anyway due to type erasure. Passing this to the binding library would be like saying "a list of maybe nullable java.lang.Object please" but worse. Imagine Map<String, List<Int>>where you now lost all information that would make deserializing this possible.

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

The following does notcompile:

以下编译:

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

The error message could be clearer to say "only classes without generic parameters are allowed on the left side of ::"

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

And your use of javaClasscan only be used on an instance, so if you happen to have had a list handy...

并且您的使用javaClass只能用于实例,所以如果您碰巧手边有一个列表......

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

Then you would end up with a type erased Class<T>which again is the wrong thing to use here, but valid syntax.

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

So what does the code @edwin is showing actually do?

那么@edwin 显示的代码实际上做了什么?

By creating an object with a super type ParameterizedTypeReferencethe code works around this problem because any class, even if type erased, can see the generics in its superclass and interfaces via the lense of Type. For example:

通过创建一个具有超类型ParameterizedTypeReference的对象,代码可以解决这个问题,因为任何类,即使类型被擦除,也可以通过Type. 例如:

val xyz = object : MySuperClass<SomeGenerics>() {}

This instance of an anonymous class has the superclass MySuperClasswith generic parameters of SomeGenerics. So if MySuperClasscontained this code:

匿名类的这个实例MySuperClass具有泛型参数为的超类SomeGenerics。因此,如果MySuperClass包含此代码:

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

You could then add .typeto the end of our declaration to access this functionality:

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

val typeOfSomeGenerics = object : MySuperClass<SomeGenerics>() {}.type

And now you would have some implementation of Typedescribing our SomeGenericsclass. It would be one of: Class, ParameterizedType, GenericArrayType, TypeVariable, and WildcardType

现在你将有一些Type描述我们SomeGenerics类的实现。它将是以下之一: Class, ParameterizedType, GenericArrayType, TypeVariable, 和WildcardType

And by understanding and working with these, a library that does data binding knows enough to do its job.

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

Life is easier in Kotlin:

在 Kotlin 中生活更轻松:

In Kotlin, it is easy to write extension functions so that you never have to do this type of code more than once. Just create a helper inline function that uses reified generics to pass through the type and create the ParameterizedTypeReference:

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

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

And now you can change @edwin's example to:

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

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

回答by Edwin Dalorzo

I think you need to actually use the RestTemplate.exechangemethod that has a signature that accepts a ParameterizedTypeReference<T>where Tcan be the generic type of your response (in your case a List<User>)

我认为您需要实际使用RestTemplate.exechange具有接受 a ParameterizedTypeReference<T>whereT可以是您的响应的通用类型的签名的方法(在您的情况下 a List<User>

Suppose I have an endpoint that returns a list of Jedi names in JSON format, as follows

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

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

And I would like to invoke this endpoint and get a List<String>as the result, with the list containing the Jedi names from the JSON.

我想调用这个端点并得到一个List<String>结果,列表中包含来自 JSON 的绝地名称。

Then I can do this:

然后我可以这样做:

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]

Notice that my ParameterizedTypeReferencehas a type argument of List<String>. That's what does the trick.

请注意, myParameterizedTypeReference的类型参数为List<String>。这就是诀窍。

And that worked for me when I tried it.

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

回答by Juan Rada

You may try

你可以试试

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

回答by Leno Tavares Cabral

First, you will make a new class - UserList.kt (the name is our owner)

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

Inside:

里面:

class UserList : MutableList<User> by ArrayList()

After that, you can call your method that has a RestTemplate

之后,您可以调用具有RestTemplate

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

It may be returns null, so you need to take care of that.

它可能是 Returns null,所以你需要照顾它。