Kotlin: generics and variance

Viewed 1047

I want to create an extension function on Throwable that, given a KClass, recursively searches for a root cause that matches the argument. The following is one attempt that works:

fun <T : Throwable> Throwable.getCauseIfAssignableFrom(e: KClass<T>): Throwable? = when {
    this::class.java.isAssignableFrom(e.java) -> this
    nonNull(this.cause) -> this.cause?.getCauseIfAssignableFrom(e)
    else -> null
}

This works too:

fun Throwable.getCauseIfAssignableFrom(e: KClass<out Throwable>): Throwable? = when {
    this::class.java.isAssignableFrom(e.java) -> this
    nonNull(this.cause) -> this.cause?.getCauseIfAssignableFrom(e)
    else -> null
}

I call the function like so: e.getCauseIfAssignableFrom(NoRemoteRepositoryException::class).

However, Kotlin docs about generics says:

This is called declaration-site variance: we can annotate the type parameter T of Source to make sure that it is only returned (produced) from members of Source, and never consumed. To do this we provide the out modifier

abstract class Source<out T> {
    abstract fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // This is OK, since T is an out-parameter
    // ...
}

In my case, parameter e isn't returned, but consumed. It seems to me that it should be declared as e: KClass<in Throwable> but that doesn't compile. However, if I think of out as "you can only read from, or return, it" and in as "you can only write, or assign a value, to it", then it makes sense. Can someone explain?

3 Answers
Related