2
votes

I am trying to build a simple HttpClient in Kotlin. I got it working with generic types using inline functions with reified generic type like this:

inline fun <reified T> execute(path: String,
                               requestMethodString: HttpMethod,
                               data: String?,
                               callback: (response: T) -> Unit) {
    (...)
    callback(mapper.readValue(json.toString())) 
    //jackson using kotlin module reads type from what callback wants as 
    //callback(mapper.readValue<T>(json.toString())) 
    (...)
}

This works well if I call my HttpClient like this:

HttpClient.get<HttpResponse<Task>>(...) {    
// HttpResponse<Task> is a type of the payload returned from my rest server
// 'it' is of type HttpResponse<Task>
}

Now since every response from the server will be wrapped in HttpResponse I would like to be able to ommit it from declaring generic type of the method call but still return it in the callback

HttpClient.get<Task>(...) { /* 'it' is still of type HttpResponse<Type>*/ }

But the problem is that when I change my HttpClient to (notice the changed type for jackson readValue and callback parameter type):

inline fun <reified T> execute(path: String,
                               requestMethodString: HttpMethod,
                               data: String?,
                               callback: (response: HttpResponse<T>) -> Unit) {
    (...)
    callback(mapper.readValue(json.toString())) 
    //jackson using kotlin module reads type from what callback wants as 
    //callback(mapper.readValue<HttpResponse<T>>(json.toString())) 
    (...)
}

Jackson is no longer able to deserialize my payload as it doesn't know type anymore and fails deserializing any T which it would deserialize normally in my original example when T represented entire payload returned from the server.

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to **.********.model.Task

My question is if anyone knows how to make it work? Is the issue on the kotlin, jackson or my side? Could I build Type reference somehow to combine both types that I know during runtime thanks to reified generic type?

val argumentClass = T::class.java
val containerClass = HttpResponse::class.java
val combinedType = ???
mapper.readValue(json.toString(), combinedType)

Also 'get' method just calls 'execute' method with correct HttpMethod as I didn't show that earlier so it's not relevant (I change types there aswell when changing to HttpResponse callback)