skip to content

How does if-as-an-expression combine with single-expression functions and block branches? When would you prefer it over when?

level: seniorimportance: should knowfreq 35%

answer

  1. fun max(a,b) = if (a>b) a else b — no return
  2. Block branch value = last expression
  3. if = 2 outcomes; when = many cases
  4. when over sealed/enum = exhaustive, no else
  5. else-if ladder smell -> refactor to when

basics

~20 s

You can make a function body a single if expression with =, no return needed. Branches can be blocks whose last line is the value. Use if for two outcomes; reach for when when you have several cases.

solid answer

~50 s

Because `if`/`else` is an expression, it pairs naturally with **single-expression functions** using `=` instead of a block body: `fun max(a: Int, b: Int) = if (a > b) a else b`. No `return` is needed; the expression's value is the function's result, and the return type is inferred (declare it explicitly on public APIs). Branches may be **blocks**, where the **last expression** is the branch value — useful when a branch needs setup before producing its result. Prefer `if` for a single boolean split (two outcomes); reach for `when` for multiple discrete cases, range/type checks, or when you want **exhaustiveness** over a sealed hierarchy or enum (the compiler enforces all cases, no `else` needed). Chained `else if` ladders are a signal to refactor to `when`. Keep expression-bodied functions short; if a branch grows complex, a block body with explicit `return`s can read better.

code

kotlin · 15 lines
kotlin
// expression body + if
fun sign(n: Int) = if (n >= 0) "non-neg" else "neg"

// block branch with last-expression value
fun bonus(score: Int): Int = if (score > 90) {
    val extra = 10
    extra
} else 0

// prefer when for many cases / exhaustiveness
enum class S { OK, FAIL }
fun describe(s: S) = when (s) {
    S.OK -> "ok"
    S.FAIL -> "fail"
}

go deeper

for a junior

Writes a single-expression if function and knows it needs no return keyword.

for a middle

Explains block-branch last-expression semantics and chooses if vs when for 2 vs many cases.

for a senior

Articulates when's exhaustiveness over sealed/enum and when an else-if ladder should be refactored.

for a principal

Weighs API clarity (explicit return types, expression vs block body) and sets team conventions for if/when usage.

## if as an expression in idiomatic Kotlin ### Single-expression (expression-bodied) functions A function can be written with `=` and no braces when its body is one expression. Since `if` is an expression, it fits: ```kotlin fun max(a: Int, b: Int) = if (a > b) a else b ``` - No `return` keyword — the expression *is* the result. - The return type is **inferred**; for public/library APIs, declare it explicitly for clarity and binary stability. ### Block branches Each branch can be a `{ ... }` block; the **value of the block is its last expression**: ```kotlin fun classify(n: Int): String = if (n >= 0) { val sign = "non-negative" sign // block value } else { "negative" } ``` Intermediate lines run as side effects; only the final expression contributes the value. ### if vs when `when` is Kotlin's multi-branch selector and is also an expression. - **Use `if`** for a single boolean condition (two outcomes): `if (c) x else y`. - **Use `when`** for: - several discrete branches (replacing an `else if` ladder), - matching on ranges (`in 1..9`), types (`is String`), or arbitrary predicates, - **exhaustiveness**: over a `sealed` class/`enum`, `when` checks all cases and needs no `else`, and the compiler errors if you add a case and forget to handle it. ```kotlin when (status) { Status.OK -> handleOk() Status.FAIL -> handleFail() } // exhaustive over the enum; no else required ``` A long `else if` chain is a refactoring smell pointing at `when`. ### Style guidance - Keep expression-bodied `if` functions short and readable. - If branches accumulate logic or multiple `return` points, a block body with explicit `return` can be clearer than a sprawling expression. ### Keywords/APIs involved - `=` (expression body), inferred vs explicit return types. - `when`, `sealed`, `enum class`, `is`, `in` — the alternatives for multi-way branching and exhaustiveness.

  • Why might you declare the return type on an expression-bodied function instead of relying on inference?
    Explicit types document the API, prevent accidental type drift from refactors, and on public/library code keep the binary signature stable.
  • When does when give you something if cannot?
    Exhaustiveness over a sealed class or enum: the compiler verifies all cases are handled with no else, flagging forgotten branches at compile time.

saying these in an interview costs you the question

  • Adding return inside a single-expression function body
  • Thinking a block branch returns its first or random line
  • Defaulting to long else-if ladders instead of when
  • Claiming if can be exhaustive over a sealed type without else
  • Never declaring return types even on public APIs

context