Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin abstract class with generic param and methods which use type param

I'm trying to create an abstract class with generic parameter which will have subclasses which should call methods without having to specify type parameters. I have this so far:

abstract class AbstractClass<T : Any> @Autowired constructor(protected val delegate: MyService) {
    inline fun <T: Any> myMethod(param: Any): T? {
        return delegate.myMethod(param).`as`(T::class.java)
    }
}

And implementation:

class TesterWork @Autowired constructor(delegate: MyService) : AbstractClass<Tester>(delegate) {
}

Now when calling myMethod I have to specify the type argument:

testerWork.myMethod<Tester>("test")

I was wondering would it be possible to infer the type argument automatically? Can I somehow rework myMethod? Note that I need to have T::class.java inside the method.

like image 478
NikolaB Avatar asked Mar 15 '16 13:03

NikolaB


1 Answers

You cannot use a class' generic parameter as a reified generic (getting its T::class token), because at runtime the generic parameter is erased: Kotlin follows Java's type erasure practice and doesn't have reified generics for classes.
(Kotlin has reified generics only for inline functions)

Given that, it's up to you to pass and store a Class<T> token so that you can use it.

Also, myFunction in your example introduces a generic parameter, and it will be a new generic, not connected to the class generic parameter in any way (naming both T only adds confusion, consider them T1 and T2). If I get it right, you meant the class' generic instead.

Probably what you can do is declare an abstract val that would store a class token and rewrite the function so that it uses the stored class token:

abstract class AbstractClass<T : Any> constructor(protected val delegate: MyService) {
    protected abstract val classToken: Class<T>

    fun myMethod(param: Any): T? {
        return delegate.myMethod(param).`as`(classToken)
    }
}

Then, deriving from AbstractClass will require overriding the classToken:

class TesterWork constructor(delegate: MyService) : AbstractClass<Tester>(delegate) {
    override val classToken = Tester::class.java
}

After that, you will be able to call the function on TesterWork instance without specifying the generic parameter:

val service: MyService = ...
val t: Tester? = TesterWork(service).myMethod("test")
like image 188
hotkey Avatar answered Sep 17 '22 16:09

hotkey