Kotlin用Class< T>调用Java方法.争论 [英] Kotlin call java method with Class<T> argument

查看:654
本文介绍了Kotlin用Class< T>调用Java方法.争论的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想像这样在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的一些实现.它将是以下之一:ClassParameterizedTypeGenericArrayTypeTypeVariableWildcardType

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&lt; T&gt;调用Java方法.争论的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆