What is a smart cast in Kotlin, and how does it work after an `if (x != null)` or `is` check?
answer
- Check first, then use without a cast
- `if (x != null)` -> x is non-null inside
- `if (x is T)` -> x is T inside
- Compiler proves it, no `as` needed
- Works on stable `val`
basics
~10 sAfter you check a variable for null or for a type, Kotlin automatically treats it as the checked type inside that block. You can use it without writing an explicit cast.
solid answer
~40 sA smart cast is the compiler automatically narrowing a variable's type after a successful check, so no explicit `as` cast is needed. After `if (x != null)`, inside the `if` block `x` is treated as the non-null type (e.g. `String` instead of `String?`), so you can call `x.length` directly. After `if (x is String)`, `x` is treated as `String` inside the block. The compiler tracks this via flow analysis: it knows the check must have passed to reach that code. It works for `val` of stable types; the result is identical to writing `(x as String)` but the compiler proves it's safe, so there's no `ClassCastException` risk and no runtime cast cost beyond the original check.
code
kotlin · 10 linesfun render(value: Any?) {
if (value is String) {
// smart-cast to String
println(value.uppercase())
}
if (value != null) {
// smart-cast to Any (non-null)
println(value.hashCode())
}
}go deeper
Knows that after if (x != null) or is you can use the variable without a cast, and can write a basic example.
Explains it's compiler flow analysis on stable vals and relates it to explicit as/as?.
Distinguishes the static-type narrowing from runtime behavior, notes stability requirements, and the negative-branch / !is case.
Frames smart casts within Kotlin's flow-sensitive type system and explains why stability (val, no custom getter, same module) is required for soundness.
## What a smart cast is A **smart cast** is the Kotlin compiler **automatically narrowing the static type** of a variable after it has been **proven** to hold a certain type or to be non-null. You do not write `as`; the compiler inserts the cast for you because flow analysis guarantees it is safe. Kotlin distinguishes **nullable** types (`String?`, can hold `null`) from **non-null** types (`String`). You cannot call members on a nullable type directly. A null check removes the nullability *within the proven region*. ## After a null check ```kotlin fun describe(x: String?) { if (x != null) { // here x is smart-cast to String (non-null) println(x.length) // no ?. needed } } ``` Inside the `if (x != null)` block, `x` has type `String`, so `x.length` compiles. Outside the block it is `String?` again. ## After a type (`is`) check ```kotlin fun length(any: Any): Int { if (any is String) { // any is smart-cast to String here return any.length } return -1 } ``` The `is` operator checks the runtime type; on success the variable is treated as that type. `!is` smart-casts in the *negative* branch / after an early return. ## Why it is safe The compiler only smart-casts when it can **prove** the value cannot change between the check and the use — primarily for `val` local variables and `val` properties of stable types. The generated bytecode still contains the check; the smart cast just lets the source code skip the redundant explicit `as`. ## Relation to explicit cast `x as String` is an **explicit** cast that throws `ClassCastException` if wrong. A smart cast is the compiler doing the same narrowing for you only when it has already verified the type, so it never fails at that point. Key operators/keywords: `is`, `!is`, `as`, `as?`, `!=`, `==`.
- Does a smart cast still emit a runtime type check in the bytecode?Yes — the `is`/`!= null` check itself runs at runtime; the smart cast only removes the need for a redundant explicit `as` in the source.
- What's the difference between `as` and a smart cast?`as` is an explicit cast that can throw `ClassCastException`; a smart cast is the compiler narrowing the type only after it has already proven the type, so it cannot fail there.
Like a bouncer checking ID at the door: once you're inside, everyone knows you're of age, no one re-checks.
saying these in an interview costs you the question
- Says you still must write `as String` after the null check
- Thinks smart cast changes the runtime value rather than the static type
- Confuses smart cast with the `!!` not-null assertion operator
- Believes smart cast can throw ClassCastException at the use site