What determines the result type of an if/else expression in Kotlin? Walk through if (c) 1 else 2, if (c) 1 else null, and a branch that throws.
answer
- Type = least common supertype of branches
- Int + null = Int?
- throw/return branch = Nothing (subtype of all)
- Unrelated branch types widen toward Any
- Expected type can steer inference
basics
~20 sThe type is the closest common type that fits both branches. Two Ints give Int. Int and null give Int? (nullable). A branch that throws contributes nothing, so the type comes from the other branch.
solid answer
~50 sThe result type of an `if`/`else` expression is the **least common supertype** of the branch types — the most specific type assignable from both. `if (c) 1 else 2` → `Int`. `if (c) 1 else null` → `Int?`, because `null` forces nullability and the common supertype of `Int` and `Nothing?` is `Int?`. A branch that `throw`s or `return`s has type **Nothing**, the bottom type that is a subtype of everything; it contributes no upper bound, so `if (c) compute() else throw E()` takes the type of `compute()`. Mixing unrelated types widens the result: `if (c) 1 else "x"` resolves to a common supertype like `Any` (more precisely `Comparable<*> & Serializable`), which is usually a smell. You can pin the type explicitly with an expected type, e.g. `val x: Number = if (c) 1 else 2.0`, guiding inference toward `Number`.
code
kotlin · 4 linesval a = if (true) 1 else 2 // Int
val b = if (true) 1 else null // Int?
val c = if (true) 1 else throw RuntimeException() // Int
val d: Number = if (true) 1 else 2.0 // Numbergo deeper
Knows two Int branches give Int and that a null branch makes the result nullable.
Explains least-common-supertype inference and correctly types the null and throw cases.
Explains Nothing as the bottom type and how throwing helpers compose in if/?: without breaking inference.
Discusses steering inference via expected types and treats unrelated branch widening as a design smell to flag in review.
## How the type is computed The type of `if (c) A else B` is the **least upper bound (least common supertype)** of the types of `A` and `B`: the most specific type that both branch values can be assigned to. ### Case 1: if (c) 1 else 2 Both branches are `Int`. The common supertype is `Int`. ```kotlin val x = if (c) 1 else 2 // x: Int ``` ### Case 2: if (c) 1 else null `1` is `Int`; `null` has type `Nothing?` (nullable bottom). The least common supertype of `Int` and `Nothing?` is `Int?`. ```kotlin val x = if (c) 1 else null // x: Int? ``` You then must handle nullability (`?.`, `?:`, `!!`, smart casts) downstream. ### Case 3: a branch that throws `throw` and `return` produce type **Nothing** — the bottom type with **no instances**, a subtype of every type. A Nothing branch never yields a value, so it imposes no constraint; the expression type equals the *other* branch's type. ```kotlin val port = if (raw in 1..65535) raw else throw IllegalArgumentException("bad port") // port: Int (the throw branch is Nothing) ``` ### Mixing unrelated types ```kotlin val v = if (c) 1 else "x" // v: Any (more precisely Comparable<*> & Serializable) ``` The compiler still finds *a* common supertype, but losing the precise type is usually a design smell. ### Steering inference with an expected type An explicit target type changes how both branches are coerced: ```kotlin val n: Number = if (c) 1 else 2.0 // n: Number, not the LUB of Int/Double ``` ### Why Nothing matters Because `Nothing` is a subtype of all types, helper functions returning `Nothing` (e.g. a custom `fail(msg): Nothing`) compose cleanly inside `if`/`else` and `?:` without breaking type inference. ### Keywords/APIs involved - `Nothing` — bottom type for `throw`/`return`/non-terminating calls. - `Int?`, `String?` — nullable types arising from a `null` branch. - `?:` (Elvis), `?.` (safe call) — for consuming a nullable result.
- What is the type of null on its own?Nothing?, the nullable bottom type. Combined with a non-null branch like Int, the if expression becomes Int?.
- Why does a throw branch not make the whole expression nullable or Any?throw has type Nothing, a subtype of every type, so it adds no upper bound. The result type collapses to the other branch's type.
The result type is the smallest umbrella that covers both branches; a throwing branch needs no umbrella at all.
saying these in an interview costs you the question
- Saying if (c) 1 else null is Int rather than Int?
- Claiming a throw branch forces Any or Unit
- Not knowing Nothing is the bottom type
- Thinking mixed-type branches are a compile error rather than widening
- Ignoring that the result nullability must be handled downstream