RizTech Academy logo
RizTech Academy
Language BasicsLesson 4 of 525 min

if, when and expressions that return values

Every program makes decisions. Kotlin's tools for that — if and when — carry one idea that distinguishes the language from most others: they are expressions, meaning they produce a value, not just perform an action. Once that clicks, a lot of Kotlin code gets shorter and safer.

if as an expression

You already know if as a statement:

val hour = 20
if (hour < 12) {
    println("Good morning")
} else {
    println("Good evening")
}

But in Kotlin, if also returns a value, so you can assign its result directly:

val greeting = if (hour < 12) "Good morning" else "Good evening"
println(greeting)          // Good evening

The if is the value. This replaces the "ternary operator" (condition ? a : b) that other languages have — Kotlin does not have a ternary, because it does not need one; if/else already does the job and reads more clearly.

When a branch has several lines, the last expression in the block is its value:

val price = 250
val label = if (price > 200) {
    val tier = "premium"
    "This is a $tier seat"      // this last line is the branch's value
} else {
    "This is a regular seat"
}

One rule to remember: when you use if as an expression, the else is mandatory. Without it, what would the value be when the condition is false? The compiler will not let you leave that undefined.

when — the tool switch wishes it were

For more than two branches, when is Kotlin's answer, and it is far more capable than a Java switch:

val rating = 4
val label = when (rating) {
    5 -> "Excellent"
    4 -> "Good"
    3 -> "Average"
    else -> "Poor"
}
println(label)             // Good

Like if, when is an expression — it returns a value, assigned here to label. No break statements (a switch classic footgun where forgetting one causes "fall-through" bugs); each branch is self-contained.

when does much more than match single values:

val n = 42
val description = when {
    n < 0 -> "negative"
    n == 0 -> "zero"
    n < 10 -> "small"
    n < 100 -> "medium"
    else -> "large"
}
println(description)       // medium

With no argument after when, each branch is a boolean condition, checked top to bottom — this form replaces a long if / else if / else if chain and reads far better.

You can also match multiple values, ranges, and types in one branch:

val day = 6
val kind = when (day) {
    1, 2, 3, 4, 5 -> "weekday"     // multiple values
    6, 7 -> "weekend"
    else -> "invalid"
}

val score = 75
val grade = when (score) {
    in 90..100 -> "A"              // a range
    in 70..89  -> "B"              // 70 to 89 inclusive
    in 50..69  -> "C"
    else -> "F"
}
println(grade)             // B

in 70..89 uses a range (the next lesson's topic) and reads almost like English. Matching on type with is — is String -> ... — is where when becomes genuinely powerful, and the sealed-classes lesson builds an entire pattern on it.

The safety benefit: exhaustiveness

Here is why "expression" matters beyond brevity. When when is used as an expression, the compiler requires it to cover every case — that is what the else is doing. You cannot accidentally forget a branch and have the value be undefined; the compiler refuses:

val label: String = when (rating) {
    5 -> "Excellent"
    4 -> "Good"
    // error: 'when' expression must be exhaustive, add an 'else' branch
}

This turns "I forgot to handle a case" from a runtime surprise into a compile-time error. With sealed classes and enums, Kotlin gets even stricter and can check you have handled every possible value without needing else at all — one of the language's best safety features, covered in the classes module.

if versus when — which to use

  • Two outcomes, one condition: if/else.
  • Three or more branches, or matching one value against many: when.
  • A chain of unrelated boolean conditions: when with no argument (cleaner than if/else if/else).

Both are expressions, so prefer assigning their result over reassigning a var inside branches. This:

val label = when (rating) { 5 -> "Excellent"; 4 -> "Good"; else -> "Poor" }

is better than declaring var label and setting it inside each branch — it is a val, it cannot be left unset, and the intent is obvious. This is the val-by-default habit and the expression habit reinforcing each other.

Check your work

What makes if special in Kotlin. It is an expression — it produces a value you can assign.

Why Kotlin has no ternary operator. if/else as an expression already does the job, more readably.

What a multi-line branch returns. Its last expression.

Why else is mandatory for an if expression. Otherwise the value would be undefined when the condition is false.

What when improves over a Java switch. It is an expression, needs no break (no fall-through bugs), and matches values, ranges and types.

What when with no argument does. Treats each branch as a boolean condition, top to bottom — replacing an if/else if chain.

What exhaustiveness means, and why it is valuable. An expression when must cover every case, turning a forgotten branch into a compile-time error.

When enums/sealed classes make else unnecessary. The compiler knows every possible value and checks you handled them all.

The habit these reinforce. Assign the expression's result to a val rather than reassigning a var in branches.

Practice

  1. Write an if expression that assigns a greeting from the hour, and print it for two hours.
  2. Remove the else from that expression and read the compiler error.
  3. Write a when that maps a rating 1–5 to a label, as an expression assigned to a val.
  4. Write a when with no argument that classifies a number as negative/zero/small/medium/large.
  5. Use a range branch (in 90..100) to turn a score into a grade.
  6. Match multiple values in one branch (weekday/weekend from a day number).
  7. Write a when expression assigned to a val, deliberately omit a needed branch, and read the exhaustiveness error.
  8. Take an if/else if/else chain you have written before and rewrite it as an argument-less when. Decide which reads better.

Official documentation

Next: loops and ranges — and why you will write fewer loops than you expect.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship