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) }
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
.
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'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.
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.
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
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.
The following does not compile:
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...
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.
So what does the code @edwin is showing actually do?
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>() {}
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] }
You could then add .type
to the end of our declaration to access this functionality:
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
And by understanding and working with these, a library that does data binding knows enough to do its job.
Life is easier in 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
:
inline fun <reified T: Any> typeRef(): ParameterizedTypeReference<T> = object: ParameterizedTypeReference<T>(){}
And now you can change @edwin's example to:
val response = restTemplate.exchange(request, typeRef<List<String>>())
You may try
return restTemplate.getForObject(URI(hostAddress), Array<User>::class.java).toList()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With