How do you safely access deeply nested nullable properties using `?.`, `?:`, and `?.let`? Show how short-circuiting works across the chain.
answer
- First null in `a?.b?.c` -> whole chain null, rest skipped
- Chain type = last property made nullable
- `?:` at end for default; `?.let` to act on result
- `?.` binds tighter than `?:`
- Repeating a chain re-runs every getter
basics
~20 sChain ?. between each nullable step; if any link is null the whole chain becomes null and stops early. Add ?: at the end for a default, or ?.let { } to run code only when the final value exists.
solid answer
~40 sFor `a?.b?.c?.d`, each `?.` short-circuits: the **first** null link makes the entire expression `null` and skips the rest, so you never dereference null. The chain's static type is the last property's type made nullable. Append `?: default` to supply a fallback (`a?.b?.c ?: 0`) or `?.let { }` to act on the resolved non-null value (`a?.b?.c?.let { use(it) }`). You can mix them: `a?.b?.let { it.compute() } ?: fallback`. Watch precedence — `?.` binds tighter than `?:`, so `a?.b ?: c` means `(a?.b) ?: c`. Repeated chains re-evaluate side-effecting getters each time, so for an expensive or changing path, capture it once with `?.let { }` and work inside the block rather than writing the chain multiple times.
code
kotlin · 5 linesval name: String = country?.capital?.mayor?.name ?: "unknown"
country?.capital?.mayor?.let { mayor ->
mayor.name?.let { println("Mayor: $it") }
}go deeper
Can write a?.b?.c and add ?: for a default; may not state short-circuit or precedence rules precisely.
Explains short-circuiting, the resulting nullable type, and combines ?./?:/?.let correctly.
Considers re-evaluation cost of repeated chains, precedence pitfalls, and when to capture mid-chain with let.
Weighs deep chaining vs. restructuring the domain model (e.g. removing nullability) and codebase-wide readability.
## Building a safe chain Given nested nullables: ```kotlin class Country(val capital: City?) class City(val mayor: Person?) class Person(val name: String?) val c: Country? = ... ``` A full safe traversal: ```kotlin val mayorName: String? = c?.capital?.mayor?.name ``` ### Short-circuit semantics Each `?.` is evaluated left to right. The **first** null encountered makes the whole expression evaluate to `null` and **skips** every step to its right (they're never executed). So if `c.capital` is null, `mayor` and `name` are never accessed. The **static type** of the chain is the final property's type widened to nullable — here `String?`. ### Supplying a default with Elvis `?:` ```kotlin val name: String = c?.capital?.mayor?.name ?: "unknown" ``` If any link is null, the fallback `"unknown"` is used. Note the result is non-null (`String`). ### Acting on the value with `?.let` ```kotlin c?.capital?.mayor?.name?.let { n -> println("Mayor: $n") // runs only if every link was non-null } ``` ### Combining mid-chain `let` ```kotlin val len = c?.capital?.let { city -> city.mayor?.name?.length } ?: 0 ``` Here `let` lets you compute on `city` (non-null) and still keep the outer chain null-safe. ## Precedence gotcha `?.` (postfix) binds **tighter** than the infix Elvis `?:`. So: ```kotlin val x = a?.b ?: c // means (a?.b) ?: c — NOT a?.(b ?: c) ``` ## Re-evaluation cost Each time you write the chain, every getter along it runs again. If a getter is expensive or has side effects, don't repeat `a?.b?.c` in several places — capture once: ```kotlin a?.b?.c?.let { value -> log(value) process(value) } ``` ## When `!!` sneaks in Never "fix" a chain with `a?.b!!.c` to silence the compiler — that reintroduces a `NullPointerException` risk. Keep the chain safe or use `?:`/`let`. ## Keywords/APIs named `?.` (safe call, short-circuiting), `?:` (Elvis), `let`, `it`, smart cast, operator precedence, `!!` (avoid).
- What is the static type of `country?.capital?.mayor?.name`?`String?` — the final property's type made nullable, because any link in the chain may produce null.
- Does `a?.b ?: c` mean `(a?.b) ?: c` or `a?.(b ?: c)`?`(a?.b) ?: c`. The safe call `?.` binds tighter than the Elvis operator `?:`.
A relay race where a dropped baton ends the race — no later runner even starts.
saying these in an interview costs you the question
- Saying a later link still executes after an earlier null
- Claiming the chain's type is non-null
- Using `!!` mid-chain to silence the compiler
- Not knowing `?.` has higher precedence than `?:`
- Repeating an expensive chain instead of capturing with `let`