Search code examples
genericskotlincovariance

Covariant function parameter in Kotlin


I want to implement a function that optionally takes an Activity class and redirects to it. If no class is provided, a default will be used.

In Java I'd do

public void onComplete(@Nullable Class<? extends Activity> redirectTarget) {
    if (redirectTarget == null) redirectTarget = DefaultActivity.class;
    ContextCompat.startActivity(context, new Intent(context, redirectTarget), null);
}

Now, I tried the following in Kotlin:

fun <T : Activity> onComplete(redirectTarget: Class<in T> = DefaultActivity::class.java) {
    ContextCompat.startActivity(context, Intent(context, redirectTarget), null)
}

I thought in T would accept any subclasses of T in the parameter but in the above example, I get a warning saying

Type missmatch. 
Expected: Class <in T>
Found: Class<DefaultActivity>

How do I implement a function that accepts any Activity class?


Solution

  • This has nothing to do with variance. This is an issue with how you're trying to use generics.

    Let's say you have a function:

    fun <T> print(toPrint: T) = println(toPrint.toString())
    

    You can call this function like this:

    print<Int>(1)
    

    All is good.

    But let's say you want to have a default String parameter when the user doesn't want to specify the `toPrint' argument. Following your approach, you would do:

    fun <T> print(toPrint: T = "") = println(toPrint.toString())
    

    In a world where this is legal, what would then happen if you call the function specifying the type but not the argument? E.g.:

    print<Int>()
    

    The type of the default parameter doesn't match the type at the call site.

    So how to go about solving your issue?
    It turns out you don't even need a generic function, as you could do:

    fun onComplete(redirectTarget: Class<out Activity> = DefaultActivity::class.java) {
        ContextCompat.startActivity(context, Intent(context, redirectTarget), null)
    }