Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The Kotlin when construct facilitates type checking by evaluating a subject against specified types using the is and !is operators. It integrates directly with Kotlin’s compiler-driven smart cast system, automatically casting the subject to the matched type within the lexical scope of the successful branch without requiring explicit cast operators (as).

Syntax

when (subject) {
    is TypeA -> // subject is smart-cast to TypeA
    is TypeB -> // subject is smart-cast to TypeB
    !is TypeC -> // subject is guaranteed NOT to be TypeC
    else -> // Fallback branch
}

Technical Mechanics

1. Smart Casting and Scope When a branch condition like is String evaluates to true, the compiler tracks this type assertion. Within the right-hand side of that specific branch, the subject is treated as the asserted type. Properties and functions of that type can be invoked immediately.
fun evaluate(payload: Any): Int {
    return when (payload) {
        is String -> payload.length // 'payload' is smart-cast to String
        is List<*> -> payload.size  // 'payload' is smart-cast to List
        else -> 0
    }
}
2. Smart Cast Guarantees and Mutable Variables Smart casting inside a when block relies on the compiler’s ability to guarantee that the subject variable cannot change between the type check and its usage.
  • It succeeds for local val variables, function parameters, and val properties of the same module without custom getters.
  • It succeeds for local var variables, provided the compiler can guarantee they are not modified between the type check and the usage, and they are not captured by a lambda that modifies them.
  • If the compiler cannot guarantee immutability (e.g., for mutable properties, open properties, or properties with custom getters), the when expression and the is check still compile and execute perfectly fine. It is only the smart cast that is denied. Compilation will only fail if the developer subsequently attempts to invoke methods or properties specific to the asserted type without manually casting it (as).
3. Inline Variable Declaration To strictly scope the subject and guarantee immutability for smart casting, Kotlin allows declaring a local val directly inside the when subject parenthesis. Kotlin’s grammar fully supports explicit type annotations for this inline variable, though the type can also be inferred.
interface Response
class Success(val data: String) : Response
class Error(val exception: Exception) : Response

fun executeRequest(): Response = Success("Data loaded")

fun process() {
    when (val response: Response = executeRequest()) {
        is Success -> println(response.data)      // 'response' is scoped only to the when block
        is Error -> println(response.exception)   // 'response' is smart-cast to Error
        else -> println("Unknown response")
    }
}
4. Exhaustiveness The compiler enforces exhaustiveness for all when expressions (where the result is assigned or returned). Since Kotlin 1.7, exhaustiveness is also strictly enforced for when statements if the subject is a sealed class, sealed interface, enum, or Boolean. An else branch is required unless the provided branches exhaustively cover all possible types:
  • Open Types: For open types (like standard non-sealed classes), an else branch is generally required when the construct is used as an expression. The Kotlin compiler does not perform logical exhaustiveness checking for complementary checks (e.g., is String and !is String). However, if the subject is of a non-nullable type, providing an is Any branch does satisfy the compiler’s exhaustiveness requirement, and no else branch is needed (though the compiler will emit a warning that the check is always true).
  • Sealed Types: For sealed classes or sealed interfaces, the compiler resolves all possible subtypes at compile time. If all subtypes are checked using is, the else branch is safely omitted.
sealed class State
class Loading : State()
class Loaded(val data: String) : State()

fun getState(): State = Loading()

fun render() {
    // Exhaustive expression: No 'else' branch needed because State is sealed
    val uiText = when (val currentState = getState()) {
        is Loading -> "Please wait..."
        is Loaded -> currentState.data // Smart-cast to Loaded
    }
}
5. Evaluation Order Branches are evaluated sequentially from top to bottom. The construct terminates evaluation as soon as the first matching is or !is condition is met. If a subject implements multiple interfaces or inherits from multiple classes checked in the block, only the first matching branch executes.
Master Kotlin with Deep Grasping Methodology!Learn More