Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In kotlin, how to return an instance defined by generic class parameter

I'm trying to write a nice Kotlin wrapper for a web framework against kotlin 1.0.3. In that I am trying to mixin a function to the request to have it return a bean via a JSON transformation using jackson.

So in my library I have the following

private val mapper: ObjectMapper = ObjectMapper().registerModule(KotlinModule())
fun <T : Any> Request.asDataBean(type: KClass<T>): T = mapper.readValue(this.body(), type.java)

But when I goto use the code as such

post("/hello", { req, res ->
    val bean = req.asDataBean(TestBean::class)
})

It errors saying that the expected value of bean is Any. What I want is for my API to work as above where whatever the generic "class" definition that is passed into asDataBean method is the type of value that is returned back.

I've also tried

fun <T> Request.asDataBean(type: KClass<*>): T = mapper.readValue(this.body(), type.java) as T

as well as changing the usage code to

val bean: TestBean = req.asDataBean(TestBean::class)

in hopes of making it work but they also give the exact same error when using the code.

How do I get it to use the generic defined by the class type passed in as the parameter (very similar to how all the spring api's work in java)?

like image 202
Ryba Avatar asked Jul 11 '16 05:07

Ryba


People also ask

Can you create instances of generic type parameters?

Yes, this is nice especially if the generic class is abstract, you can do this in the concrete subclasses :) @TimKuipers The <E> in class Foo<E> is not bound to any particular type.

How do you get a class from generic type Kotlin?

There are no direct ways to do this in Kotlin. In order to check the generic type, we need to create an instance of the generic class<T> and then we can compare the same with our class.

How do I call generic method Kotlin?

Kotlin generic example When we call the generic method <T>printValue(list: ArrayList<T>) using printValue(stringList), the type T of method <T>printValue(list: ArrayList<T>)will be replaced by String type.

How do you declare a generic variable in Kotlin?

We can make generic variable using this kind of syntax: "val destinationActivity: Class<*>". Main part is "*".


1 Answers

post function in your example requires route: (Request, Response) -> Any parameter, that is a function, taking request and response and returning some non-null value.

When you use a lambda expression as a route, its return type is inferred from the last expression of lambda's body, and since in Kotlin an assignment is not an expression, the following lambda doesn't have the return type at all:

{ req, res ->
    val bean = req.asDataBean(TestBean::class)
}

To make it work just make bean the last expression

{ req, res ->
    val bean = req.asDataBean(TestBean::class)
    bean
}

or do not use the assignment at all:

{ req, res -> req.asDataBean(TestBean::class) }

Note: I have used the following definition of asDataBean function:

fun <T: Any> Request.asDataBean(type: KClass<T>): T =
    mapper.readValue(this.body(), type.java)

Also you could make a reified overload, which calls non-reified one, so that you don't have to expose all the internals:

inline fun <reified T: Any> Request.asDataBean(): T = 
    asDataBean(T::class)

req.asDataBean<TestBean>() // usage
like image 64
Ilya Avatar answered Sep 22 '22 20:09

Ilya