Smart Casts

Smart Casts and Platform Types from Java

After a check such as x != null or x is String, the compiler smart casts the variable to the narrower type inside the branch, provided the value cannot change in between. A var property of a class can, so if (line.note != null) line.note.length fails with "smart cast to 'String' is impossible, because 'note' is a mutable property that could be mutated concurrently"; copy it into a local val first.

A value from Java, whose types carry no ?, arrives as a platform type, shown as String!: Kotlin lets you treat it either way, at your own risk. The JDK's unannotated System.getenv() shows it:

smart.kt: smart casts in when, and a platform type from JavaKotlin
fun describe(x: Any?): String = when (x) {
    null -> "nothing"
    is String -> "text of ${x.length} chars"   // x is smart-cast to String here
    is Int -> "a number, doubled ${x * 2}"
    else -> "a ${x::class.simpleName}"
}
fun main() {
    println(listOf(null, "Gift wrap", 41, 2.5).map(::describe))
    val api: String? = System.getenv("BOOKNEST_API")   // declared nullable: safe
    println(api?.length ?: "BOOKNEST_API is not set")
    val raw = System.getenv("BOOKNEST_API")            // String!: compiles unchecked
    println(raw.length)
}
Output
[nothing, text of 9 chars, a number, doubled 82, a Double]
BOOKNEST_API is not set
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()"
  because "raw" is null
   at SmartKt.main(smart.kt:12)
   at SmartKt.main(smart.kt)

Give Java values an explicit Kotlin type on entry, as api does. Kotlin also reads JSpecify, JetBrains and Android nullability annotations, so an annotated @Nullable String arrives as String?.