Smart Casts

The Compiler Casts for You

After an is check, Kotlin automatically treats the variable as that type inside the checked branch - no manual cast needed. This is a smart cast. It also works after &&, ||, early return, and in when.

Example

fun lengthOf(x: Any): Int {
    if (x is String) return x.length        // x is String here
    if (x !is List<*>) return -1            // early return
    return x.size                           // x is List<*> here
}

fun main() {
    println(lengthOf("Kotlin"))
    println(lengthOf(listOf(1, 2, 3)))
    println(lengthOf(42))

    val v: Any = "hello"
    if (v is String && v.length > 3) println(v.uppercase())   // smart cast after &&
}

Output

6
3
-1
HELLO

When Smart Casts Do Not Work

A smart cast only works if the compiler can prove the value cannot change between the check and the use. It works for local vals, local vars not changed in between, and val properties in the same module without a custom getter. It does not work for var class properties (another thread could change them) or open/custom-getter properties.

Example

Fix: copy the property into a local val first.

class Screen {
    var message: Any? = "Welcome back"

    fun show() {
        // if (message is String) println(message.length)   // error: smart cast impossible
        val m = message                                     // local copy
        if (m is String) println("${m.length} chars: $m")
    }
}

fun main() {
    Screen().show()
}

Output

12 chars: Welcome back