100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Kotlin

Smart Casts in Kotlin

After a null-check or type-check, Kotlin's compiler automatically treats a variable as the narrowed type within that scope, no explicit cast needed.

Null SafetyIntermediate8 min readJul 8, 2026
Analogies

Introduction

In many languages, checking a variable's type or nullability doesn't change how the compiler treats it afterward; you still need an explicit cast to use it as the narrower type. Kotlin removes this redundancy with smart casts: once the compiler proves, through a null-check or an is type-check, that a variable has a certain type within a given scope, it automatically treats the variable as that type for the rest of the scope. This is possible because Kotlin's compiler performs flow-sensitive analysis, tracking what is known about a variable at each point in the code.

🏏

Cricket analogy: Once an umpire confirms a delivery was a no-ball, the scorer doesn't need to re-check on every subsequent action within that phase, similar to how Kotlin's compiler, after an is or null check, automatically treats a variable as the narrower type for the rest of the scope.

Syntax

kotlin
fun printLength(text: String?) {
    if (text != null) {
        // text is smart-cast to String (non-null) here
        println(text.length)
    }
}

fun describe(obj: Any) {
    if (obj is String) {
        // obj is smart-cast to String here
        println(obj.uppercase())
    }
}

Explanation

A smart cast happens when the compiler can guarantee that a check performed earlier in the code still holds true at the point of use. After if (x != null), the compiler knows x cannot be null inside that branch, so it lets you call non-null-only methods on x directly, without ?. or !!. Similarly, after if (obj is String), the compiler knows obj is a String inside that branch, so it exposes String's members without an explicit as String cast. Smart casts also work with when expressions and with the && / || operators in a single condition. However, smart casts are not reliable for var properties that could be modified by another thread between the check and the use, nor for properties with custom getters, since the compiler cannot guarantee the value hasn't changed; in those cases you still need an explicit cast or a local copy of the value.

🏏

Cricket analogy: After the umpire confirms 'not out' (x != null), the scorer can record the run directly without a second check, just as Kotlin lets you call methods on x without ?. inside that branch; but if a runner (a var open to change) could be substituted mid-over, you can't trust the earlier check.

Example

kotlin
fun describe(input: Any?) {
    when {
        input == null -> println("input is null")
        input is Int -> println("Int doubled: ${input * 2}")
        input is String -> println("String length: ${input.length}")
        else -> println("Unknown type")
    }
}

fun main() {
    describe(21)
    describe("Kotlin")
    describe(null)
}

Output

kotlin
Int doubled: 42
String length: 6
input is null

Key Takeaways

  • Smart casts let the compiler treat a variable as a narrower or non-null type after a check, without an explicit cast.
  • They work after null-checks (if (x != null)) and type-checks (if (x is String)).
  • Kotlin's compiler tracks flow-sensitive information to know when a smart cast is safe.
  • Smart casts also apply inside when expressions and combined boolean conditions.
  • Smart casts are unreliable for mutable var properties that another thread could modify, and for properties with custom getters.

Practice what you learned

Was this page helpful?

Topics covered

#Kotlin#KotlinProgrammingStudyNotes#Programming#SmartCastsInKotlin#Smart#Casts#Syntax#Explanation#StudyNotes#SkillVeris