Loslegen
Javaneer
Zurück zur Stufe
Stufe 1·Syntax für Java-Augen

Alles ist ein Ausdruck

if, when und try geben in Kotlin Werte zurück, also gibt es keinen Ternary-Operator und keinen Temporär-dann-Zuweisen-Tanz - und when ist ein weit mächtigeres switch.

14 Min. LesezeitFortgeschritten

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

Here's a shift that quietly changes how you write code: in Kotlin, control-flow constructs are expressions - if, when, and try return values. Java draws a hard line between statements (things that do something) and expressions (things that have a value); Kotlin erases much of it. The practical upshots are no ternary operator (you don't need one), no "declare-then-assign-in-each-branch" dance, and when - a switch so much better it deserves its own introduction.

if is an expression

In Java, if is a statement, so to compute a value conditionally you either use the ternary ?: or declare a variable and assign it in each branch. In Kotlin, if itself yields a value:

val max = if (a > b) a else b          // if returns a value - no ternary needed

val label = if (score >= 90) {         // branches can be blocks; the LAST expression is the value
    "excellent"
} else if (score >= 60) {
    "pass"
} else {
    "fail"
}

Because if/else produces a value, Kotlin has no ternary operator - it would be redundant. And you avoid the common Java pattern of String label; if (...) label = ...; else label = ...; - just assign the if expression directly to a val. (Note: for if to be an expression, an else is required - there must be a value in every case.)

when: switch, evolved

when is Kotlin's replacement for switch, and it's dramatically more capable. As an expression, it returns a value; and it matches far more than constants:

val description = when (x) {
    0 -> "zero"                        // constant match
    1, 2, 3 -> "small"                 // multiple values in one branch
    in 4..10 -> "medium"               // a RANGE
    is String -> "it's a string"       // a TYPE check (with smart cast)
    else -> "large"                    // else is required when used as an expression
}

when can match constants, multiple values, ranges (in 4..10), type checks (is String - and it smart-casts, so inside that branch x is a String), and even arbitrary conditions when you omit the subject:

val grade = when {                     // no subject: each branch is a boolean condition
    score >= 90 -> "A"
    score >= 80 -> "B"
    else -> "F"
}

Compare Java's switch: no fall-through bugs (no break needed - branches don't fall through), no restriction to constants, and it returns a value. It's closer to Java's newer switch expressions and pattern matching, but it arrived earlier and is used everywhere in idiomatic Kotlin.

try is an expression too

Even try/catch returns a value, which is occasionally elegant:

val number = try {
    input.toInt()                      // the value if it succeeds
} catch (e: NumberFormatException) {
    0                                  // the value if it fails
}

Why this matters: fewer temporaries, more val

The cumulative effect of expression-oriented control flow is that you assign the result of a decision directly to an immutable val, instead of declaring a mutable var and mutating it across branches. That reinforces the val-by-default habit from the last lesson: expressions let you compute-then-bind, so more of your variables can be read-only. Less mutation, fewer temporaries, and code that reads as "this value is the result of this decision."

Exhaustive when on sealed types is compiler-checked

when becomes especially powerful over a sealed type or enum: if you cover every case, you don't need an else, and if you later ADD a case, the compiler flags every when that no longer handles all of them. That's exhaustiveness checking - the same algebraic-data-type benefit from the advanced Java module, and a reason when + sealed classes (next module) is a favorite Kotlin pattern for modeling states safely.

A vending machine that hands you the item, vs. one that just beeps

Java statements are like a vending machine that, when you press a button, merely does something internally - you then have to go check a separate slot to see what happened (declare a variable, inspect it after). Kotlin expressions are a machine that hands you the item directly the moment you choose: assigning val snack = when(choice) ... - the selection immediately becomes the thing you hold. You don't press a button and then rummage; the act of deciding is the act of producing the value. when is the deluxe machine that also accepts ranges of coins, groups of buttons, and 'anything sweet' as a selection - far more than the old model's one-button-one-item switch - and it never accidentally dispenses two snacks because you forgot a break.

Rewrite Java statements as Kotlin expressions

Convert this Java to idiomatic Kotlin using expressions and when: a method that takes an int httpStatus and returns a String category - 200-299 → 'success', 300-399 → 'redirect', 400-499 → 'client error', 500-599 → 'server error', anything else → 'unknown'. The Java version declares a String result, uses if/else-if, and returns it. Make the Kotlin as tight as idiomatic style allows.

What does it mean that if, when, and try are expressions in Kotlin?

Key takeaways

  • In Kotlin, if, when, and try are expressions that return values, so you assign the result directly to a val.
  • Because if returns a value, Kotlin has no ternary operator, and you avoid the declare-then-assign-per-branch pattern (an else is required for if used as an expression).
  • when replaces switch and is far more capable: it matches constants, multiple values, ranges (in 4..10), and type checks (is String, with smart cast), and returns a value.
  • when has no fall-through (no break) and can be subject-less, where each branch is a boolean condition - a cleaner if/else-if chain.
  • Expression-oriented control flow reinforces val-by-default (compute then bind) and, over sealed types/enums, gives compiler-checked exhaustiveness.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten