skip to content

Show how takeIf chained with the Elvis operator expresses a conditional value, and explain why this is preferred over an if/else here.

level: middleimportance: must knowfreq 50%

answer

  1. Pattern: x.takeIf { p } ?: fallback
  2. Elvis substitutes when left is null
  3. RHS can be error/throw/return
  4. Avoids repeating the receiver
  5. Don't over-stack — prefer when/if for long logic

basics

~10 s

Use value.takeIf { condition } ?: fallback. If the condition holds you get the value, otherwise the fallback. It reads as one fluent expression instead of a multi-line if/else.

solid answer

~40 s

The idiom is `receiver.takeIf { predicate } ?: fallback`. `takeIf` produces `T?` — the receiver or `null` — and the Elvis operator `?:` substitutes the `fallback` whenever the left side is `null`. This keeps everything as a single expression you can chain further (`.let`, `.also`, etc.) and assign directly to a `val`. Compared with `if (predicate) receiver else fallback`, the `takeIf`/`?:` form avoids repeating the receiver, works neatly inside larger call chains, and lets the right-hand side of `?:` be an expression that throws (`?: error(...)`) or returns early (`?: return`). The cost: it allocates nothing thanks to `inline`, but readability degrades if the predicate is long or if you stack multiple `takeIf`s — at that point a plain `if`/`when` is clearer.

code

kotlin · 5 lines
kotlin
fun resolveTimeout(raw: Int?): Int =
    raw?.takeIf { it > 0 } ?: DEFAULT_TIMEOUT

fun requireToken(header: String): String =
    header.takeUnless { it.isBlank() } ?: error("Authorization header required")

go deeper

for a junior

Can write the x.takeIf { } ?: fallback pattern and read it correctly.

for a middle

Explains that takeIf yields T? and Elvis supplies the default, and knows ?: short-circuits and can throw/return.

for a senior

Articulates the composability and no-repetition benefits and the readability limits of stacking takeIf.

for a principal

Sets guidance on when expression-style nullable chaining improves a codebase versus when explicit if/when is mandated for clarity and reviewability.

## The idiom The canonical pattern is: ```kotlin val result = receiver.takeIf { predicate } ?: fallback ``` ## How it works step by step 1. `takeIf { predicate }` returns the receiver if `predicate` is true, else `null`. Its static type is `T?`. 2. The **Elvis operator `?:`** evaluates its left operand; if it is non-null it yields that value, otherwise it yields the right operand (`fallback`). The right operand is only evaluated when needed (short-circuit). 3. The whole thing is **one expression**, so it can be assigned, returned, or chained. ```kotlin val port: Int = rawPort.takeIf { it in 1..65535 } ?: 8080 val user = repo.findById(id).takeIf { it.isActive } ?: throw NotFoundException(id) val trimmed = input.trim().takeIf { it.isNotEmpty() } ?: return null ``` ## Why prefer it over if/else here - **No repetition of the receiver.** `if (input.isNotEmpty()) input else "-"` mentions `input` twice; `input.takeIf { it.isNotEmpty() } ?: "-"` mentions it once. - **Composability.** Because the result is still an expression, you can keep chaining: `text.takeIf { it.isNotBlank() }?.uppercase() ?: "NONE"`. - **Powerful right-hand sides.** `?: error(...)`, `?: return`, `?: throw`, or a computed default all work because Elvis takes any expression (including `Nothing`-typed ones). ## When NOT to use it - The predicate is long or has side effects — a `when`/`if` block reads better. - You stack several `takeIf`/`takeUnless` calls; nested nullability becomes confusing. - The two branches produce genuinely different shapes — `if`/`when` makes the branching explicit. ## Gotcha: null receivers `takeIf` is itself callable on a nullable receiver via `?.`: `maybeNull?.takeIf { ... }`. Without the `?.`, calling `takeIf` on a `T?` would pass a possibly-null `it` into the predicate.

  • Rewrite if (s.isNotBlank()) s else "-" using takeIf.
    s.takeIf { it.isNotBlank() } ?: "-"
  • Can the Elvis right-hand side throw?
    Yes — it can be any expression, including one of type Nothing such as throw, error(...), or return.

saying these in an interview costs you the question

  • Claiming takeIf alone provides the fallback (it returns null, not the default)
  • Forgetting that the result before ?: is nullable
  • Stacking many takeIf calls where if/when would be clearer
  • Saying Elvis evaluates both sides eagerly (it short-circuits)

context