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
Kotlin call java method with Class<T> argument
提问by mojtab23
I want to use Spring RestTemplate
in 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 List
in val ret = List<User>.javaClass
.
该RestTemplate.getForObject(URI url, Class<T> responseType)
有这个签名,我从得到这个错误“未解决的参考序列”List
在val ret = List<User>.javaClass
。
If I use like this val ret = List<User>::class.java
I 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 (Class
vs. 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 ParameterizedTypeReference
in one, and TypeReference
in another, TypeRef
in 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 javaClass
can 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 ParameterizedTypeReference
the 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 MySuperClass
with generic parameters of SomeGenerics
. So if MySuperClass
contained this code:
匿名类的这个实例MySuperClass
具有泛型参数为的超类SomeGenerics
。因此,如果MySuperClass
包含此代码:
abstract class MySuperClass<T> protected constructor() {
val type: Type = (javaClass.genericSuperclass as ParameterizedType)
.actualTypeArguments[0]
}
You could then add .type
to the end of our declaration to access this functionality:
然后,您可以添加 .type
到我们声明的末尾以访问此功能:
val typeOfSomeGenerics = object : MySuperClass<SomeGenerics>() {}.type
And now you would have some implementation of Type
describing our SomeGenerics
class. 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.exechange
method that has a signature that accepts a ParameterizedTypeReference<T>
where T
can 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 ParameterizedTypeReference
has 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
,所以你需要照顾它。