Skip to content
Ameen Abozaid
PublishedBEG-012-L04

Switch expressions (modern Java)

  • Lesson 4 of 6
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

The classic switch has two weaknesses: a forgotten break makes it fall through, and to get a value out of it you must assign a variable in every branch. Modern Java fixes both with the arrow form case X -> ..., which never falls through, and with the switch expression, a switch that produces a value you can store directly.

Think of the junction again. The classic switch is a junction without barriers; the arrow form builds a barrier at every exit automatically. And a switch expression is a junction that hands you a ticket at the exit with the answer written on it.

This lesson builds on Lesson 3 (labels, break, default, fall-through) and on Module 9 (variables and types). It matters for your career because switch expressions are the foundation for matching on types in later Java versions, which the course reaches in the Junior level.

Learning Objectives

By the end of this lesson you will be able to:

  • Write a switch expression that returns a value and assign it to a variable.
  • Use the arrow form, including several labels in one case, as a statement and as an expression.
  • Explain why the arrow form cannot fall through and needs no break.
  • Explain why a switch expression needs a default (or otherwise full coverage) and read the compiler error when it lacks one.
  • Use a block with yield when one case needs several statements.
  • Convert a classic switch to a switch expression, and decide which to use.

Written Explanation

The arrow form

java
switch (day) {
    case 6, 7 -> System.out.println("Weekend");
    default   -> System.out.println("Weekday");
}

Differences from the classic form:

  • Each label is followed by -> and then one action: an expression, a block in braces, or a throw (which you will learn later).
  • Only that action runs. There is no fall-through and no break.
  • Several constants can share a rule with commas: case 6, 7 ->. You no longer stack empty labels.

You cannot mix the two styles in one switch. A case 1 -> next to a case 2: is rejected by the compiler (Common Mistakes).

The switch expression

A switch can be used as an expression, meaning it has a value, so it can sit on the right side of an assignment:

java
String type = switch (day) {
    case 6, 7 -> "Weekend";
    default -> "Weekday";
};

Notice the semicolon after the closing brace: this is one statement, String type = ...;. Each rule's right-hand side is the value for that case, and all values must be compatible with the type you assign to.

It must cover every case

A statement may do nothing if no label matches. An expression cannot: it must always produce a value. So the compiler requires a switch expression to be exhaustive: every possible selector value must be handled. For an int or a String that means a default, because there are far too many values to list. If you forget it, the compiler says so (Example 3). With enums and sealed types, which come later, you can be exhaustive without a default by listing all the possibilities; a later module uses this.

This requirement is a safety feature: you can no longer forget the "else" case, and the variable always gets a value.

Several statements: blocks and yield

Usually a rule is one expression. When a case needs more steps, use a block and end it with yield, which hands the value to the expression:

java
String plan = switch (day) {
    case 1, 2, 3, 4, 5 -> {
        int hours = 8;
        yield "Work " + hours + "h";
    }
    case 6 -> "Half day";
    default -> "Off";
};

yield is like return for the switch: it ends the block and supplies the value. Keep blocks short; if one grows large, the logic probably belongs somewhere else.

The arrow form as a statement

You can use the arrow form without producing a value, as in the first snippet. It is a statement and does not have to be exhaustive. It is the modern replacement for the classic switch in many codebases because there is no break to forget.

Version note

Switch expressions were previewed in JDK 12 and 13 and became a standard feature in Java 14 (JEP 361). Any JDK from version 14 onward supports them, including the one used for this course's examples (JDK 21). If you ever work in a project that still compiles with Java 8 or 11, only the classic form exists there; check the project's Java version before using the arrow form.

Examples

Example 1: weekend or weekday as a value (simple)

java
int day = 6;
String type = switch (day) {
    case 6, 7 -> "Weekend";
    default -> "Weekday";
};
System.out.println(type);

Output: Weekend. Compare with Lesson 3's version: no break, no stacked labels, no variable declared ahead of time and assigned in several places.

Example 2: days in a month and a command word (practical)

java
int m = 2;
int days = switch (m) {
    case 4, 6, 9, 11 -> 30;
    case 2 -> 28;
    default -> 31;
};
System.out.println("Month " + m + ": " + days);

Output: Month 2: 28. The classic version took twelve lines; this takes five and cannot fall through. As in Lesson 3, leap years are ignored here.

With a String selector and a block:

java
int day = 6;
String plan = switch (day) {
    case 1, 2, 3, 4, 5 -> {
        int hours = 8;
        yield "Work " + hours + "h";
    }
    case 6 -> "Half day";
    default -> "Off";
};
System.out.println(plan);

Output: Half day.

Example 3: forgetting default, and the arrow as a statement (realistic)

A developer writes a switch expression that handles only weekend days:

java
int day = 3;
String t = switch (day) {
    case 6, 7 -> "Weekend";
};

The compiler stops it (real message from JDK 21):

text
L04b.java:4: error: the switch expression does not cover all possible input values
        String t = switch (day) {
                   ^

Adding default -> "Weekday"; fixes it. The compiler has just saved you from a program that would have had no answer for 3.

Duplicate labels are still caught:

text
L04d.java:13: error: duplicate case label
            case 1 -> "b";
            ^

Finally, the earlier menu example from Lesson 3 as an arrow statement, where a missing break is impossible:

java
int option = 2;
switch (option) {
    case 1 -> System.out.println("Open");
    case 2 -> System.out.println("Save");
    case 3 -> System.out.println("Exit");
    default -> System.out.println("Unknown");
}

Output: Save only. Compare with the output Save followed by Exit that the classic version without break produced.

Backend Insight

Services often translate one code into another: an order status into a label for the customer, an HTTP status into a category, a plan name into a limit. A switch expression expresses "this value maps to that value" in one readable block, and its exhaustiveness check turns forgotten cases into compile errors instead of production bugs.

Later versions of Java and the sealed types you will meet in the Junior level extend this: when a switch expression covers every variant of a type, the compiler tells you when someone adds a new variant and the switch is no longer complete. That is a powerful way to keep business rules in sync with the data they handle, and it starts here with a plain default.

One practical note: a default in a switch expression that silently returns a harmless value can hide bad input. For values that should never occur, a later module on exceptions shows how to fail loudly instead.

Common Mistakes

Mistake 1: Mixing arrows and colons

  • Incorrect: case 1 -> ... and case 2: in the same switch.
  • Correct: pick one style per switch.

Real message:

text
L04e.java:6: error: different case kinds used in the switch
            case 2: System.out.println("b");
            ^

Mistake 2: Missing the semicolon after a switch expression

  • Incorrect: closing the braces without ; when the switch is on the right of =.
  • Correct: };

Real message:

text
L04c.java:7: error: ';' expected
        }
         ^

Mistake 3: No default in an expression over int or String

See Example 3. Add default. The compiler error is a feature, not an obstacle.

Mistake 4: Putting break in an arrow rule out of habit

In the arrow form a break is not needed, and you cannot use it to leave a switch expression. If a case needs several statements, use a block and yield.

Mistake 5: Types that do not match the assignment

int days = switch (m) { case 2 -> "short"; default -> 31; }; mixes a String and an int, so there is no single type for the value. Make every rule produce the same kind of value.

Exercises

Level A: Foundation

A1. What are two things the arrow form does better than the classic form?

Show solution A1

A1. No fall-through, so no break to forget; and several constants can share a rule with a comma. A switch expression can also produce a value directly.

A2. What does this print?

java
int n = 7;
String s = switch (n) {
    case 1, 3, 5, 7, 9 -> "odd digit";
    case 0, 2, 4, 6, 8 -> "even digit";
    default -> "not a digit";
};
System.out.println(s);
Show solution A2

A2. odd digit, because 7 is listed in the first rule.

A3. Why does a switch expression over an int need a default?

Show solution A3

A3. An expression must always produce a value, and an int has far too many possible values to list, so default covers the rest. The compiler rejects the switch otherwise.

Level B: Application

B1. Rewrite this classic switch as a switch expression assigned to String name:

java
switch (n) {
    case 1: name = "one"; break;
    case 2: name = "two"; break;
    case 3: name = "three"; break;
    default: name = "many";
}
Show solution B1

B1.

java
String name = switch (n) {
    case 1 -> "one";
    case 2 -> "two";
    case 3 -> "three";
    default -> "many";
};

B2. Write a switch expression that gives a ticket price from a String category: "child" costs 5, "adult" costs 10, "senior" costs 7, anything else costs 10.

Show solution B2

B2.

java
int price = switch (category) {
    case "child" -> 5;
    case "adult" -> 10;
    case "senior" -> 7;
    default -> 10;
};

Letter case matters: "Child" would fall to default.

B3. The code below does not compile. Say why and fix it in two different ways.

java
int level = 2;
String access = switch (level) {
    case 1 -> "read";
    case 2 -> "write";
    case 3 -> "admin";
};
Show solution B3

B3. The expression is not exhaustive: an int can be 4, 0, or anything else. Fix 1: add default -> "none";. Fix 2: replace the case for 3 by default -> "admin"; if level 3 is the only remaining possibility you care about (less clear, since other values would also get admin). Fix 1 is better.

Level C: Challenge

C1. Write a switch expression that maps a month number to a quarter string Q1 to Q4 using grouped labels, with default returning invalid. List the test values you would use and the expected results.

Show solution C1

C1.

java
String quarter = switch (month) {
    case 1, 2, 3 -> "Q1";
    case 4, 5, 6 -> "Q2";
    case 7, 8, 9 -> "Q3";
    case 10, 11, 12 -> "Q4";
    default -> "invalid";
};

Tests: 1 and 3 give Q1, 4 gives Q2, 9 gives Q3, 10 and 12 give Q4, 0 and 13 give invalid.

C2. Given char op and two int values a and b, write a switch expression that computes +, -, *, and / and returns 0 for anything else. Explain what happens for '/' with b = 0 and what you would want to happen in a real program.

Show solution C2

C2.

java
int result = switch (op) {
    case '+' -> a + b;
    case '-' -> a - b;
    case '*' -> a * b;
    case '/' -> a / b;
    default -> 0;
};

With integer division, a / 0 makes the program stop with an ArithmeticException at run time. In a real program you would check b != 0 first with an if and report an error, because returning 0 for a bad operator or a division by zero hides a problem. Exceptions are covered in a later module.

Mini Challenge

In main, write a plan pricing calculation using a switch expression: from a String plan ("free", "basic", "pro") produce a monthly price (0, 9, 29) and, in a second switch expression, a user limit (1, 5, 50). Anything else should give a clearly marked fallback value (such as -1) so that bad input is visible.

Check your work: a typo like "Pro" produces the fallback, you wrote the expected values before running, and neither switch has a break.

Summary & Next Lesson

Key takeaways

  • The arrow form case X -> ... runs only its own rule: no fall-through and no break.
  • A switch expression produces a value and ends with a semicolon when assigned.
  • Commas let several constants share one rule.
  • A switch expression must cover every case; for int and String that means default.
  • Use a block with yield when a rule needs several statements.
  • It needs Java 14 or later.

Next lesson

You can now pick the right tool for each decision. The next lesson, "Taming nested conditions", deals with decisions inside decisions: how to flatten deep if nesting using &&, ||, early exits, and named conditions.

Self-check

  1. What replaces break in the arrow form?
  2. What must a switch expression do that a switch statement need not?
  3. How do you give two constants the same rule in the arrow form?
  4. When do you need yield?
  5. Which Java version made switch expressions a standard feature?
Show answers
  1. Nothing: each arrow rule runs alone, so no break is needed.
  2. Cover every possible input value and always produce a value.
  3. Separate them with commas: case 6, 7 -> ....
  4. When a rule is a block with several statements and must hand back a value.
  5. Java 14.

References

  • OpenJDK, JEP 361: Switch Expressions. Confirms that switch expressions became a standard feature in Java 14 after previews in JDK 12 and 13.
  • Oracle, The Java Language Specification, Java SE 21, Chapter 14. Supports the statement that arrow rules do not fall through while old-style labels do.
  • The exhaustiveness rule, yield, and the compiler messages were verified by compiling and running the examples with JDK 21; I did not find a separate page to cite for exhaustiveness.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.