kotlingenericsinlinekotlin-reified-type-parameters

How to a class with generics with an inline refied function


If I have a class like the following:

class SimpleClass {
    var test: Int = 1
}

I can do something like this:

inline fun <reified T : SimpleClass> myFunction1(): T {
        //Do something with T
}

But if I have a class with generics:

class ComplexClass<T> {
    var test: T? = null
}

How can I do the same?

The following doesn't work:

inline fun <reified T : ComplexClass<C>> myFunction2(): T {
        //Do something with T
        //Do something with C
}

How can I do it?

Here is a more complex example, if needed.


Solution

  • Let's take a look at your example

    class Mediator {
        //It does't work! My question is about this function.
        inline fun <reified T : Container<C>> mediate(string: String): T {
            if (C::class == Child1::class)
                //Do something
            if (C::class == Child2::class)
                //Do something else
        }
    } 
    
    class UseExample : Mediator() {
    
        fun example1(): Container<Child1> {
            return mediate("test1") // your mediate function does not take
            // any parameters to determine the generic type
        }
    
        fun example2(): Container<Child2> {
            return mediate("test2")
        }
    }
    

    To perform something with the type C which is used to create Container<C> and perform something with the result type which you represent as T : Container<C> you only need to know the C. Since reified can only be used to keep the type if it is known during the compile time at the call site, rewrite your function signature like this.

    inline fun <reified C> mediate(string: String): Container<C> {
       // now you know the C and you know the T which is Container<C>
       if (C::class == Child1::class) ...
       // Since T depends on C, you can only check the C type to perform logic
    }
    

    Use it like following

    fun example1(): Container<Child1> {
        return mediate<Child1>("test") // Note that know your C 
        // type is known at the function call therefore compiler 
        // can substitute it with exact type
    }
    

    Here is my minimal example from Kotlin Playground

    class Container<T>(t: T) {
        val smth : T = t
    }
    
    class Mediator {
        inline fun <reified C> mediate(string: String): Container<C> {
             if (C::class == Int::class) {
                 println("Int")
                 return Container<Int>(1) as Container<C>
             }
    
             throw IllegalStateException("Yopta")
         }
    }
    
    
    fun main() {
        val m = Mediator()
    
        m.mediate<Int>("ABC") // Output is "Int"
    }