Is there a way of avoiding having to specify the s...
# announcements
a
Is there a way of avoiding having to specify the same class twice in the code below? I have a generic abstract class, and I need to know what's the actual type (because I want to use it when deserializing with Jackson's
ObjectMapper
). So I know that I need to pass the class as an argument. But then, I also need to specify the type
T
when instantiating the generic base class. Is there a way of making the compiler infer any of the two?
Copy code
data class ExamplePayload(val value: String, val otherValue: Int)

abstract class Worker<T : Any>(private val kclass: KClass<T>) {
    private lateinit var objectMapper: ObjectMapper

    fun handleMessage(message: String) {
        val payload = objectMapper.readValue(message, kclass.java)
        work(payload)
    }

    protected abstract fun work(payload: T)
}

// Here I need to specify ExamplePayload twice:
class ExampleWorker : Worker<ExamplePayload>(ExamplePayload::class) {
    override fun work(payload: ExamplePayload) {
        TODO("not implemented")
    }
}
l
Can you use the
T::class.java
if you inline the function and retify the
T
? Something like here: https://stackoverflow.com/a/34463352
a
Which function should I reify?
handleMessage
? The issue is that this is actually a
@RabbitListener
, so the method itself is called from spring. I'm not sure much control I would have over it
s
If you don't mind using reflection you can find out the parent's class actual type arguments
Copy code
abstract class Worker<T>

class WhatAmI : Worker<String>() {
  val type = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments[0]
}

fun main() {
 println(WhatAmI().type)
}
a
That's interesting, thank you!
a
You should avoid reflection where it is not needed
See this code:``` abstract class MyWorker<T : Any> { private lateinit var objectMapper: ObjectMapper inline fun <reified V : T> handleMessage(message: String) { handleMessage(message, V::class.java) } fun <V : T> handleMessage(message: String, clazz: Class<V>) { val payload = objectMapper.readValue(message, clazz) work(payload) } protected abstract fun work(payload: T) } // Here I need to specify ExamplePayload twice: class MyExampleWorker : MyWorker<ExamplePayload>() { override fun work(payload: ExamplePayload) { TODO("not implemented") } }```
And if you cannot inline handleMessage, then:
Copy code
abstract class MyWorker<T : Any> {
        private lateinit var objectMapper: ObjectMapper

        abstract fun handleMessage(message: String)

        inline fun <reified V : T> doWorkWithMessage(message: String) {
            doWorkWithMessage(message, V::class.java)
        }

        fun <V : T> doWorkWithMessage(message: String, clazz: Class<V>) {
            val payload = objectMapper.readValue(message, clazz)
            work(payload)
        }

        protected abstract fun work(payload: T)
    }

    // Here I need to specify ExamplePayload twice:
    class MyExampleWorker : MyWorker<ExamplePayload>() {
        override fun handleMessage(message: String): Unit = doWorkWithMessage<ExamplePayload>(message)

        override fun work(payload: ExamplePayload) {
            TODO("not implemented")
        }
    }
s
He said he can't change the method sig to inclide the type param