skip to content

How does a switch expression differ from a switch statement in Java, and what does this reveal about the expression/statement distinction?

level: seniorimportance: should knowfreq 40%

answer

  1. Statement = action, no value; expression = value (assign/return)
  2. Arrow (->) labels, no fall-through, must be exhaustive = expression
  3. Colon (:) labels, fall-through, value-less = classic statement
  4. yield returns a value from a switch-expression block
  5. Same keyword, two roles -> divide is being relaxed

basics

~20 s

A switch statement performs branching but produces no value. A switch expression (Java 14+) evaluates to a value you can assign or return, using arrow labels (case A -> ...) or yield. So the same keyword can be either a statement (does an action) or an expression (produces a value).

solid answer

~50 s

Traditionally switch was only a statement: it branched based on a selector and executed code per case, with colon labels, fall-through, and no value. Java 14 standardized the switch expression, which evaluates to a single value you can use directly: int days = switch (month) { case JAN, MAR -> 31; default -> 30; };. Switch expressions use arrow labels with no fall-through, must be exhaustive (cover all cases, often via an enum or a default), and use the yield keyword to return a value from a block body. The deeper point is that switch can now appear in both roles: as a statement executed for its effect, or as an expression that produces a value usable on the right of an assignment or in a return. This is part of Java's gradual move toward more expression-oriented constructs (alongside the ternary operator and expression-bodied lambdas), softening the historically strict statement/expression divide while keeping plain if and the classic switch statement value-less.

code

java · 21 lines
java
enum Day { MON, TUE, WED }

// switch STATEMENT: performs an action, yields nothing
void greet(Day d) {
    switch (d) {
        case MON: System.out.println("start"); break; // break stops fall-through
        default:  System.out.println("other");
    }
}

// switch EXPRESSION: produces a value (assigned / returned)
int code(Day d) {
    return switch (d) {            // arrow form, exhaustive over the enum
        case MON -> 1;
        case TUE -> 2;
        case WED -> {
            int x = 3;
            yield x;               // yield gives the block's value
        }
    };
}

go deeper

for a junior

Knows that a classic switch chooses among cases and that newer Java lets switch also yield a value, with a different arrow syntax.

for a middle

Can write both forms, knows arrow labels avoid fall-through, and uses a switch expression to assign/return a value.

for a senior

Explains exhaustiveness, yield vs return, no fall-through, and frames switch as evidence the statement/expression divide is being relaxed; chooses the right form per use.

for a principal

Positions switch expressions within the broader expression-orientation trend (lambdas, pattern matching), reasons about immutability/boilerplate trade-offs, and guides team adoption and style (arrow-only, expression when computing values).

## Recap of the divide An **expression** produces a value; a **statement** performs an action and (classically in Java) has no value. The `switch` construct is the clearest place where Java now lets the *same keyword* play **both** roles. ## The switch statement (classic, all versions) ```java switch (day) { case MONDAY: case TUESDAY: System.out.println("early week"); break; // needed to stop fall-through default: System.out.println("other"); } ``` Properties: - Uses **colon (`:`) labels**. - **Fall-through**: without `break`, execution continues into the next case — a frequent bug source. - **Produces no value** — it is purely a statement executed for its side effects. - Need not be exhaustive. ## The switch expression (standardized in Java 14, JEP 361) ```java int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; // arrow label, multiple constants case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; }; // note the trailing semicolon: it's an expression ``` Properties: - **Evaluates to a value** — here assigned to `numLetters`; it could equally be `return switch (...) { ... };`. - **Arrow (`->`) labels**: no fall-through, so no `break` needed; multiple constants separated by commas. - **Must be exhaustive**: every possible selector value must be handled — for an enum that means all constants (or a `default`); otherwise it won't compile. This exhaustiveness is enforced *because* the construct must yield a value for every input. - For multi-line logic per case, use a **block with `yield`** to produce the value: ```java int size = switch (shape) { case CIRCLE -> 1; case POLYGON -> { int s = computeSides(); yield s; // 'yield' returns the value of this branch } }; ``` - `yield` is a contextual keyword that gives a value *out of a switch expression block*; `return` would exit the whole method, which is different. ## Why this matters for the expression/statement concept Before Java 14, if you wanted a value chosen by multi-way branching you had to either: ```java int n; // mutable temporary switch (day) { case MONDAY: n = 6; break; ... } // statement + mutation ``` or chain ternaries. The switch *expression* lets the branching construct itself **be a value**, so you can write `final int n = switch(...) {...};` with no mutable temporary. This is a concrete example of the language **relaxing the strict statement/expression divide**: a construct historically locked to the statement category gained an expression form. ## The broader trend Java has steadily added expression-oriented features: - The **ternary operator** `c ? a : b` — the original value-producing branch. - **Expression-bodied lambdas** `x -> x + 1` (the body is an expression) vs **block-bodied** `x -> { return x + 1; }` (statements). - **Switch expressions** (Java 14) and **pattern matching in switch** (later releases), which produce values. Meanwhile, plain `if`, `for`, `while`, and the classic `switch` *statement* remain value-less. So Java is not fully expression-oriented (like many functional languages where *everything* is an expression); it selectively adds expression forms where they reduce boilerplate and mutability. ## Practical guidance - Prefer the **switch expression** when you are computing/returning a value — it is exhaustive (compiler-checked), avoids fall-through bugs, and removes the mutable temporary. - Use the **switch statement** when you are performing side effects per case and producing no value. - Don't mix arrow and colon styles in one switch. - Remember `yield` returns from the switch expression; `return` returns from the method.

  • Why must a switch expression be exhaustive while a switch statement need not be?
    Because an expression must yield a value for every possible input — if some selector value matched no branch, there would be no value to produce. The compiler therefore requires all enum constants to be covered or a default present. A switch statement produces no value, so an unmatched selector simply does nothing, which is permitted.
  • What's the difference between yield and return inside a switch expression?
    yield produces the value of the switch expression branch and continues with the enclosing code (e.g. the assignment). return exits the entire enclosing method immediately. Use yield to supply the expression's value from a block-bodied case; use return only if you genuinely want to leave the method.

saying these in an interview costs you the question

  • Saying switch expressions can fall through (arrow form does not)
  • Using return instead of yield to produce a switch-expression value
  • Forgetting switch expressions must be exhaustive
  • Claiming if/for can also be used as expressions in Java (they cannot)
  • Thinking Java is fully expression-oriented like a functional language

context