How does if-as-an-expression combine with single-expression functions and block branches? When would you prefer it over when?
answer
- fun max(a,b) = if (a>b) a else b — no return
- Block branch value = last expression
- if = 2 outcomes; when = many cases
- when over sealed/enum = exhaustive, no else
- else-if ladder smell -> refactor to when
basics
~20 sYou 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 sBecause `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// 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
Writes a single-expression if function and knows it needs no return keyword.
Explains block-branch last-expression semantics and chooses if vs when for 2 vs many cases.
Articulates when's exhaustiveness over sealed/enum and when an else-if ladder should be refactored.
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