Type checking tests whether a value has a particular runtime type. In Kotlin, use `is`: after a successful check, the compiler treats the value as that type in the checked branch. This smart cast avoids a separate explicit cast and helps prevent class-cast failures. section 2.3.6 section 1.4.3
Evaluate a typed expression
`if (e is Num) return e.value` can access `value` directly because `e` is smart-cast to `Num`. Likewise, after `e is Sum`, `e.left` and `e.right` are available for recursive evaluation. A smart cast requires a value that cannot have changed after the check; class properties must be `val` without custom accessors. section 2.3.6
Listing 2.18 Evaluating expressions with an if cascade
fun eval(e: Expr): Int {
if (e is Num) {
val n = e as Num #1
return n.value
}
if (e is Sum) {
return eval(e.right) + eval(e.left) #2
}
throw IllegalArgumentException("Unknown expression")
}
fun main() {
println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
// 7
}
Compare an explicit `as Num` cast with the smart-cast access available after an `is Num` check, then follow the recursive `Sum` case. section 2.3.6
Generic type arguments are erased at runtime. A check such as `is List<String>` is therefore not generally allowed: Kotlin can determine that a value is a `List`, but not whether its elements are strings or integers. Use `is List<*>` to test the raw collection shape, or rely on a type argument already known by the function signature. section 11.2.1
Use checks with generic collections carefully:
- Check `is List<*>` when only the collection kind matters; `*` represents unknown type arguments. section 11.2.1
- Do not assume `as? List<Int>` verifies element types: a list of strings can pass the list cast and fail later when used as numbers. section 11.2.1
- Branch on `List<Int>` and `Set<Int>` when `Collection<Int>` already establishes the element type. section 11.2.1




