Smart cast with type parameter?

I have the following piece of code, and it requires casting this to Foo<S, String> in the first branch of the when statement to work.

inline fun <S, reified T> Foo<S, T>.bar() {
    when (T::class) {
        String::class -> {
            this as Foo<S, String>
            this.baz = quxString()
        }
        else -> {
            this.baz = quxObject()
        }
    }
}

fun <S> quxString(): (S, String) -> Unit = { _, _ -> println("quxString") }
fun <S, T> quxObject(): (S, T) -> Unit = { _, _ -> println("quxObject") }

class Foo<S, T> {
    var baz: (S, T) -> Unit = { _, _ -> println("Default") }
}

My question is: Why can’t I write it like this?

inline fun <S, reified T> Foo<S, T>.bar() {
    this.baz = when (T::class) {
        String::class -> quxString()
        else -> quxObject()
    }
}

Is not the compiler smart enough to know that T is of type String?

You can write what you want if you cast return type of quxString() (which is (S, String) -> Unit) to expected type (S, T) -> Unit.

inline fun <S, reified T> Foo<S, T>.bar() {
    this.baz = when (T::class) {
        String::class -> (quxString<S>()) as ((S, T) -> Unit)
        else -> quxObject()
    }
}
1 Like