Safe Calls and the Elvis Operator
The ?. and ?: operators that let you work with nullable values concisely instead of writing a null check every time.
阅读需 2 分钟
Once a value's type is nullable (covered in the previous lesson), you need a way to actually work with it without wrapping every single access in an if (x != null) block. Kotlin gives you two operators built exactly for this.
The safe call operator: ?.
?. calls a method or accesses a property only if the receiver isn't null — and short-circuits to null instead of throwing if it is:
val nickname: String? = getNickname()
val length = nickname?.length
println(length) // either the length, or null — never a crashlength here has type Int?, not Int — the compiler can see that the whole expression might produce null, so it propagates the nullability forward. This matters: you can't accidentally forget that length might be absent, because its type says so.
Safe calls chain naturally, short-circuiting at the first null:
data class Address(val city: String?)
data class User(val address: Address?)
val user: User? = getUser()
val city = user?.address?.cityIf user is null, or user.address is null, the whole chain evaluates to null instantly — none of the later property accesses even run, and nothing throws.
The Elvis operator: ?:
?: provides a fallback value to use when the expression on its left is null — read it as "or else":
val nickname: String? = getNickname()
val displayName = nickname ?: "Anonymous"
println(displayName) // the nickname, or "Anonymous" if it was nullIt gets its name from turned-sideways resemblance to Elvis Presley's hair and eye — ?: — though the name matters less than the pattern, which comes up constantly:
fun greet(name: String?) {
val safeName = name ?: "Guest"
println("Welcome, $safeName!")
}
greet("Alice") // Welcome, Alice!
greet(null) // Welcome, Guest!Combining both
The two operators are frequently used together — safe-call through a chain, then Elvis to supply a default if anything along the way was null:
val city = user?.address?.city ?: "Unknown city"This one line replaces what would otherwise be several nested null checks, while remaining exactly as safe: there is no path through this code that can throw a NullPointerException.
Elvis for early returns
?: isn't limited to plain values — the right-hand side can be any expression, including return or throw:
fun processOrder(order: Order?) {
val validOrder = order ?: return
// From here on, validOrder is guaranteed non-null
println("Processing order ${validOrder.id}")
}Together, ?. and ?: are how idiomatic Kotlin handles "this might not exist" — concisely, and without ever losing the compiler's safety guarantees. The next lesson covers the one escape hatch that bypasses all of this, and why it's best avoided.