kotlin reified generic in virtual function
Asked Answered
B

2

10

In my concrete class I would like to have a function that has the following signature.

inline fun <reified T : Comparable<T>> get(key: String): T

However I want to extract an interface for this class so I could swap implementations. However I'm not sure how to go about it as I can't mark virtual functions as inline.

So far I have this interface:

interface ModuleSettings {

    fun <T : Comparable<T>> get(key: String) : T

}

And this concrete class

class DefaultModuleSettings : ModuleSettings {

    override inline fun <reified T : Comparable<T>> get(key: String): T {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

But now the compiler complains that im trying to override the generic type with reified keyword. Is there any way I can achieve this pattern?

Benedetta answered 3/2, 2020 at 10:58 Comment(0)
V
12

You can't inline interface functions, but you can take the class object as a parameter. To still get the same function signature, you could create an inline reified overload in DefaultModuleSettings that delegates to the interface function. You can't call that if you only have a reference with the interface-type though.

Something like this:

interface ModuleSettings {
    fun <T : Comparable<T>> get(key: String, clazz: KClass<T>) : T
}

class DefaultModuleSettings : ModuleSettings {

    override fun <T : Comparable<T>> get(key: String, clazz: KClass<T>): T {
        TODO("not implemented")
    }

    inline fun <reified T : Comparable<T>> get(key: String): T = get(key, T::class)

}

Edit:

Making the inline function an extension function is better:

inline fun <reified T : Comparable<T>> ModuleSettings.get(key: String): T = get(key, T::class)
Vitia answered 3/2, 2020 at 11:13 Comment(4)
There's no purpose for function overloading in concrete class, as only the interface is visible for the interacting components. See my answer, it's very similar but makes the method actually visible from the interfaceBenedetta
Yes, that's the bad part as I explained. Using the extension function is better :)Vitia
Ok, I guess there's really no other way to do this in kotlin, so I'm going to accept this answer. Damn you JVMBenedetta
The problem with an extension function is if you have a generic interface they you also need to provide the types for that when calling the function.Iraqi
B
8

I kind of achieved similar behavior with extension function:

interface ModuleSettings {

    fun <T : Comparable<T>> get(key: String, type: KClass<T>): T

}

inline operator fun <reified T : Comparable<T>> ModuleSettings.get(key: String) = get(key, T::class)


class DefaultModuleSettings : ModuleSettings {

    override fun <T : Comparable<T>> get(key: String, type: KClass<T>): T {
        TODO("not implemented") 
    }
}

It's not exactly what I'm was expecting to achieve, but it works semantically. However I would still consider it a hack

Benedetta answered 3/2, 2020 at 11:13 Comment(2)
I just thought about using an extension function right after I posted my answer. This is probably the best you will get.Vitia
I don't think it's a hack. Using extensions is one of most important koltin lang practices. I'd recommend to use them often, it greatly simplifies the code and makes it more readable while compensating many language design problems, like this one.Entophyte

© 2022 - 2024 — McMap. All rights reserved.