Home > Mobile >  How to specify "accepts generic sub-type of a generic type"?
How to specify "accepts generic sub-type of a generic type"?

Time:04-19

I am trying to implement a function definition that accepts a generic parameter, as long as it extends another specific generic type. In short; param A must extend param B, where A and B are both generic.

My sample example below

abstract class Endpoint<T>(
) {

    private val myList: MutableMap<Class<T>, MutableList<((T) -> Unit)>> = mutableMapOf()

    fun addToList(
        index: Class<E>, <-- E extends T
        value: (E) -> Unit
    ) {
        myList[index] = (myList[index] ?: mutableListOf()).apply { add(value) }
    }

}

Usage example would be

Some sealed class

sealed class MainSealedClass {
    data class ChildClass(val someParam: Int): MainSealedClass()
}

And the function call

anEndpointInstance.addToList(MainSealedClass.ChildClass::class.java, "some value")

I would like to not having to define E in the abstract class declaration, since T is already defined there.

I tried doing the following as well:

Define myList as accepting keys that extend T as such

val myList: MutableMap<Class<in T>, MutableList<String>>

Define E as a T type (couldn't find how to specify it extends T in the function

    fun <E:T> addToList(
        index: Class<E>,
        value: (E) -> Unit
    ) {
        myList[index] = (myList[index] ?: mutableListOf()).apply { add(value) }
    }
    fun addToList(
        index: Class<in T>,
        value: (E) -> Unit
    ) {
        myList[index] = (myList[index] ?: mutableListOf()).apply { add(value) }
    }
    fun <E> addToList(
        index: Class<E>,
        value: (E) -> Unit
    ) where E : T {
        myList[index] = (myList[index] ?: mutableListOf()).apply { add(value) }
    }

But it never works. Is there a way of achieving this? I could not find anything in StackOverflow that had an answer for this scenario.

CodePudding user response:

You got your variance inverted. If you want your map to accept Classes of subtypes of T, then it needs to be <out T>. If you're not inspecting the keys, you could probably just put Class<*>.

Your first way of declaring the function was correct.

abstract class Endpoint<T>(
) {

    private val myList: MutableMap<Class<out T>, MutableList<String>> = mutableMapOf()

    fun <E : T> addToList(
        index: Class<E>,
        value: String
    ) {
        myList.getOrPut(index, ::mutableListOf).add(value)
    }

}
  • Related