Kotlin用Class< T>调用Java方法.争论 [英] Kotlin call java method with Class<T> argument
问题描述
我想像这样在Kotlin中使用Spring RestTemplate
:
I want to use Spring RestTemplate
in Kotlin like this:
//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
收到此错误未解决的引用列表".
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
.
如果我这样使用val ret = List<User>::class.java
,则会收到此错误类字面量的左侧仅允许类"
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"
出什么问题了?这样做的正确方法是什么?是虫子吗?
what's the problem? and what's the proper way for doing this? Is it a bug?
推荐答案
@edwin的答案正确,您遇到了 XY问题一个>你实际上是在问错事的地方.值得庆幸的是,您在执行此操作时遇到了一个错误,这导致您进入此处,@ edwin能够解释执行正确行为的替代方法.
@edwin's answer is correct, you had an XY Problem where 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.
每个Java绑定库都有类似的模式(Class
与类型引用).因此,这是好东西,您可以在您的库中学习和查找,例如RestTemplate,Jackson,GSON等.实用程序类可能在一个中为ParameterizedTypeReference
,在另一个中为TypeReference
,在另一个中为TypeRef
,依此类推;等等.但所有人都在做同一件事.
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, Jackson, 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.
现在,对于任何想知道原始代码有什么问题的人,创建一个通用的擦除类引用(如Class<T>
),语法将是:
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:
val ref = List::class.java
您将注意到,无法表达列表中项目的通用类型.即使可以,它们也会由于类型擦除而被擦除.将此传递给绑定库就像说"请为可能为空的java.lang.Object的列表",但更糟.想象一下Map<String, List<Int>>
,您现在丢失了所有可能反序列化的信息.
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.
以下不编译:
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 ::"
javaClass
的使用只能在实例上使用,因此,如果碰巧有一个方便的列表...
And your use of javaClass
can only be used on an instance, so if you happen to have had a list handy...
val ref = listOf("one", "two", "three").javaClass
然后,您将得到一个擦除为Class<T>
的类型,这再次在这里使用是错误的事情,但语法有效.
Then you would end up with a type erased Class<T>
which again is the wrong thing to use here, but valid syntax.
那么@edwin显示的代码实际上是做什么的?
通过创建具有超级类型ParameterizedTypeReference
的对象,代码可以解决此问题,因为即使删除了类型,任何类都可以通过Type
的镜头看到其父类和接口中的泛型.例如:
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:
val xyz = object : MySuperClass<SomeGenerics>() {}
此匿名类实例具有具有SomeGenerics
通用参数的超类MySuperClass
.因此,如果MySuperClass
包含以下代码:
This instance of an anonymous class has the superclass MySuperClass
with generic parameters of SomeGenerics
. So if MySuperClass
contained this code:
abstract class MySuperClass<T> protected constructor() {
val type: Type = (javaClass.genericSuperclass as ParameterizedType)
.actualTypeArguments[0]
}
然后您可以在声明的末尾添加.type
来访问此功能:
You could then add .type
to the end of our declaration to access this functionality:
val typeOfSomeGenerics = object : MySuperClass<SomeGenerics>() {}.type
现在您将具有描述我们的SomeGenerics
类的Type
的一些实现.它将是以下之一:Class
,ParameterizedType
,GenericArrayType
,TypeVariable
和WildcardType
And now you would have some implementation of Type
describing our SomeGenerics
class. It would be one of: Class
, ParameterizedType
, GenericArrayType
, TypeVariable
, and WildcardType
通过了解和使用这些知识,进行数据绑定的库知道足以完成其工作.
And by understanding and working with these, a library that does data binding knows enough to do its job.
在Kotlin中生活更轻松:
在Kotlin中,编写扩展函数很容易,因此您不必重复执行这种类型的代码.只需创建一个使用内联泛型的助手内联函数来传递类型并创建ParameterizedTypeReference
:
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
:
inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}
现在您可以将@edwin的示例更改为:
And now you can change @edwin's example to:
val response = restTemplate.exchange(request, typeRef<List<String>>())
这篇关于Kotlin用Class< T>调用Java方法.争论的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!