How does chaining safe calls like a?.b?.c work, and what is the result type? What happens if b is null in the middle?
answer
- left-to-right, stop at first null
- every nullable link needs its own ?.
- plain . is fine after a non-null link
- result type = last member type, nullable
- later side effects skipped on short-circuit
basics
~20 sYou can stack ?. through a chain. If any link is null, the whole chain stops and becomes null. The final result is nullable. So a?.b?.c is null if a, a.b, or a.b.c is null.
solid answer
~40 sChained safe calls evaluate left to right and short-circuit at the first null. In a?.b?.c, Kotlin evaluates a; if null, the entire expression is null and nothing further runs. Otherwise it reads a.b; if that is null, it stops and yields null; otherwise it reads .c. The overall type is the type of c made nullable, e.g. C?. Each ?. is its own guard, so you don't need a ?. after a link that is already non-null — but you DO need it on every link whose declared type is nullable. A common mistake is writing one ?. and a plain . later (a?.b.c), which fails to compile if b is nullable, because .c on a nullable b isn't allowed.
code
kotlin · 3 linesval name: String? = country?.capital?.mayor?.name
// null if country, capital, or mayor is null
// .name uses plain dot only if mayor.name is non-null typedgo deeper
Knows a null anywhere in the chain makes the result null.
Explains per-link guards, short-circuit of side effects, and result nullability.
Articulates when plain . is legal mid-chain and how type inference drives where ?. is mandatory.
Weighs deep safe-call chains vs. modeling absence explicitly (e.g., restructuring types) for readability and maintainability.
## Chaining safe calls Safe calls compose. A chain like `a?.b?.c?.d` is read **left to right**, and each `?.` is an independent guard. The chain **short-circuits**: at the first `null` receiver, evaluation stops and the whole expression becomes `null`. ```kotlin class Country(val capital: City?) class City(val mayor: Person?) class Person(val name: String) val c: Country? = null val name: String? = c?.capital?.mayor?.name // null, stops at c ``` ### Where do you need `?.`? You need `?.` on **every link whose static type is nullable**. If a property is declared non-null, a plain `.` is fine after it: ```kotlin // capital: City? (nullable), mayor: Person? (nullable), name: String (non-null) c?.capital?.mayor?.name // ^^^ ^^^ each nullable link needs ?. // .name uses plain . because Person.name is non-null ``` Writing `c?.capital.mayor` would **not compile**: `capital` is `City?`, so `.mayor` on it is illegal — you must write `?.mayor`. ### Result type The whole chain's type is the **last member's type, made nullable**. `name` is `String`, but `c?.capital?.mayor?.name` is `String?` because any earlier null collapses the result to null. ### Short-circuit detail Once a `null` is hit, **no later side effects run**. If links were function calls (`a?.foo()?.bar()`), `bar()` is skipped entirely when `foo()` returns null. This makes chains safe but also means you can't rely on later calls executing. ### Collapsing back to non-null Chains are usually finished with Elvis (`?: default`) or `?.let { ... }` to turn the `T?` into a usable non-null value — but those are separate operators layered on top of the chain.
- Does a?.foo()?.bar() call bar() if foo() returns null?No. The chain short-circuits at the null returned by foo(); bar() is never invoked.
- Why might a?.capital.mayor fail to compile?If capital is nullable, you cannot use a plain . to access .mayor on it; you must use ?.mayor.
saying these in an interview costs you the question
- Saying the chain throws if a middle link is null
- Claiming you only need one ?. at the start of the chain
- Thinking later calls still run after a null short-circuit
- Saying the chain result is non-null
- Confusing where plain . is allowed vs. where ?. is required