2.2 Switch Statements and Switch Expressions

Key Takeaways

  • Selector expressions in standard Java SE 17 accept byte, short, char, int, their respective boxed wrappers, String, and enums; long, float, double, and boolean are disallowed.

  • Case labels must be compile-time constants such as literals, constant expressions, or final variables initialized at declaration; non-constant variables cause compilation failures.

  • Classic switch statements using colon (:) syntax exhibit fall-through across cases unless terminated by break or return, and share a single variable scope across all cases.

  • Arrow labels (->) never fall through and allow comma-separated case values; switch expressions return values directly or with the yield keyword in multi-statement blocks, while colon-form switch expressions still fall through.

  • Switch expressions that produce a value must be exhaustive; a default branch is required unless all constants of an enum selector are explicitly handled.

Last updated: October 2026

2.2 Switch Statements and Switch Expressions

The switch construct in Java provides multi-way branching based on the value of a selector expression. Since switch expressions were standardized in Java 14 (JEP 361), the language supports two label forms (colon case X: and arrow case X ->) and two uses of switch (as a statement that performs actions, or as an expression that produces a value with yield where needed). Java SE 17 LTS includes both. The 1Z0-829 exam tests deep knowledge of permitted selector types, compile-time constant requirements for case labels, fall-through rules, variable scoping, and exhaustiveness checks.

Note

Pattern matching for switch (e.g., case Integer i -> ...) was a preview feature in Java 17 (JEP 406) and was not finalized until Java 21 (JEP 441). The Java SE 17 Developer exam (1Z0-829) covers standard, non-preview language features: classic switch statements and standard switch expressions.


Permitted and Disallowed Selector Types

Java restricts the data types that may serve as the selector expression in a switch construct.

Permitted Selector TypesDisallowed Selector Types
byte, short, char, int (primitives)long (and java.lang.Long)
Byte, Short, Character, Integer (wrappers)float (and java.lang.Float)
java.lang.Stringdouble (and java.lang.Double)
enum typesboolean (and java.lang.Boolean)
Arbitrary Object references

The null Selector Trap

If the selector expression evaluates to null at runtime—whether it is a boxed wrapper (such as Integer) or a String reference—Java immediately throws a NullPointerException upon entering the switch construct:

String input = null;

// Throws NullPointerException before checking any case or default!
switch (input) { 
    case "admin":
        System.out.println("Administrator");
        break;
    default:
        System.out.println("Guest");
}

The switch does not fall into the default branch when the selector is null. A runtime exception is thrown immediately.


Classic Switch Statements: Constant Expressions & Fall-Through

In classic switch statements, each case label must be a compile-time constant expression whose type is assignment-compatible with the switch selector:

  1. Literals: e.g., case 1:, case "READY":.
  2. Compile-Time Constant Variables: A variable declared final and initialized at the point of declaration with a constant expression (e.g., final int BASE = 10;).
  3. Enum Constants: When switching on an enum type, the case label must specify the unqualified name of the enum constant (e.g., case NORTH:, not case Direction.NORTH:).
final int MAX = 100;
final int UNASSIGNED;
UNASSIGNED = 50; // Blank final: NOT a compile-time constant!
int dynamicValue = 20;

int test = 10;
switch (test) {
    case MAX:             // Legal: compile-time constant
        break;
    // case UNASSIGNED:   // COMPILE ERROR: constant expression required
    //     break;
    // case dynamicValue: // COMPILE ERROR: constant expression required
    //     break;
}

Furthermore, the value of a case label must fit within the range of the selector's primitive type. If switching on a byte (range -128 to 127), a case label of case 130: causes a compilation error: incompatible types: possible lossy conversion from int to byte.

Fall-Through Mechanics

Classic switch branches delimited with a colon (:) exhibit fall-through. If a matching case does not terminate with a break, return, throw, or yield, execution flows sequentially into the statements of subsequent cases regardless of whether those case labels match the selector:

int day = 2;
switch (day) {
    case 1:
        System.out.print("Mon ");
    case 2:
        System.out.print("Tue ");
    case 3:
        System.out.print("Wed ");
        break;
    default:
        System.out.print("Other ");
}
// Outputs: "Tue Wed "

If day is 2, execution jumps directly to case 2:. It prints "Tue ", falls through to case 3:, prints "Wed ", and then encounters break, which terminates the switch.


Variable Scoping Across Switch Cases

A classic switch block forms a single scope for local variables between the opening { and closing }. A variable declared inside one case remains in scope for all subsequent case and default blocks:

int selector = 1;
switch (selector) {
    case 1:
        int value = 42;
        System.out.println(value);
        break;
    case 2:
        // COMPILE ERROR: variable value is already defined in scope!
        int value = 99; 
        System.out.println(value);
        break;
    default:
        // value is still in scope here, but might NOT be initialized!
        // System.out.println(value); // COMPILE ERROR if used before initialization
}

To isolate variable declarations between cases, each branch must be enclosed in its own block using curly braces:

switch (selector) {
    case 1 -> { int value = 42; System.out.println(value); }
    case 2 -> { int value = 99; System.out.println(value); } // Legal in isolated blocks
}

Modern Switch Expressions (Java 14 / Java 17 LTS)

Java SE 17 provides modern switch expressions using the arrow (->) syntax. Switch expressions resolve many long-standing design traps of classic switches:

  1. No Fall-Through: Exactly the code on the right-hand side of the matching arrow executes, so no break is needed. (A break is still legal inside an arrow block of a switch statement, but a switch expression cannot be exited with break.)
  2. Multiple Comma-Separated Labels: Multiple matching values can share a single branch separated by commas (case 1, 2, 3 ->).
  3. Value Production: A switch expression can return a value directly to a variable assignment or method call.
int month = 4;
int days = switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> 31;
    case 4, 6, 9, 11 -> 30;
    case 2 -> 28;
    default -> 0;
};

The yield Keyword

When a branch in a switch expression requires multiple statements, the logic is enclosed in a block { ... }. To return a value from this block, Java uses the contextual keyword yield:

int code = 3;
String description = switch (code) {
    case 1 -> "Low Priority";
    case 2, 3 -> {
        String prefix = "Urgent: Level ";
        yield prefix + code; // Returns value from block
    }
    default -> "Standard";
};

Critical Rules Governing yield

  • yield takes an argument and passes it as the result of the enclosing switch expression.
  • return exits the entire enclosing method, whereas yield only exits the switch expression.
  • You cannot use yield inside a classic switch statement that does not return a value.
  • You cannot mix arrow (->) syntax and colon (:) syntax within the same switch construct. A switch must use exclusively arrows or exclusively colons.
// COMPILE ERROR: cannot mix ':' and '->' in the same switch
int invalid = switch (code) {
    case 1 -> 10;
    case 2: yield 20; 
    default -> 0;
};

Exhaustive Matching Requirements

When a switch is used as an expression (evaluating to a value), the compiler strictly requires exhaustive coverage of all possible input values:

  1. Primitives and Strings: Because int, char, String, etc., have effectively boundless or large value domains, a default branch is mandatory. Omitting default results in a compile-time error: the switch expression does not cover all possible input values.
  2. Enum Types: If the switch selector is an enum, a default case is optional if and only if every enum constant defined in the enumeration is explicitly handled by a case label:
enum Status { PENDING, ACTIVE, REJECTED }

Status current = Status.ACTIVE;

// Legal without 'default' because all 3 constants are covered:
int statusCode = switch (current) {
    case PENDING -> 1;
    case ACTIVE -> 2;
    case REJECTED -> 3;
};

If a new enum constant (e.g., CANCELLED) is subsequently added to the enum definition, the switch expression will fail compilation until the new constant is handled or a default branch is provided.

When a switch is used as a statement (performing actions without producing a value), exhaustiveness is not required.

Note

Two independent choices. Fall-through depends on the label form, and exhaustiveness depends on whether the switch is an expression. A switch expression may use colon labels with yield (and then falls through like a classic switch), and a switch statement may use arrow labels (no fall-through, no exhaustiveness check). The comparison table below contrasts the two most common combinations: a colon-form statement and an arrow-form expression.


Comparison: Classic Switch vs. Switch Expression

FeatureClassic Switch StatementModern Switch Expression
Primary SyntaxColons (case VALUE:)Arrows (case VALUE ->)
Fall-ThroughYes (automatic without break)No (never falls through)
Multiple Labels per BranchStacked (case 1: case 2:) or comma-separated (case 1, 2:)Comma-separated (case 1, 2 ->)
Value ProductionCannot evaluate to a valueReturns a value via direct expression or yield
Multi-line Block Terminationbreak;yield value;
ExhaustivenessNot enforced by compilerMandatory (must cover all values or have default)
Variable ScopingShared across entire switch blockIsolated to respective block {} branches
Test Your Knowledge

Which of the following data types is NOT permitted as the selector expression in a Java SE 17 switch construct?

A

short

B

long

C

java.lang.String

D

java.lang.Character

Test Your Knowledge

Consider the following switch expression:

public class SwitchCalculation {
    public static void main(String[] args) {
        int code = 2;
        int result = switch (code) {
            case 1 -> 10;
            case 2, 3 -> {
                int factor = 4;
                yield code * factor;
            }
            default -> 0;
        };
        System.out.println(result);
    }
}

What is the outcome of compiling and running this code?

A

The code outputs 0

B

The code fails to compile because yield cannot be used inside an arrow branch block

C

The code outputs 12

D

The code outputs 8

Test Your Knowledge

What is the result of attempting to compile and run the following code?

public class SwitchScope {
    public static void main(String[] args) {
        int k = 1;
        switch (k) {
            case 1:
                int val = 100;
                System.out.print(val + " ");
                break;
            case 2:
                int val = 200;
                System.out.print(val);
                break;
            default:
                System.out.print("Def");
        }
    }
}
A

Compilation fails because val is declared twice

B

The code outputs Def because no case matches k

C

The code outputs 100 because case 1 ends with break

D

The code outputs 100 200 because case 1 falls through

Test Your Knowledge

Consider the following class:

public class NullSwitch {
    public static void main(String[] args) {
        String text = null;
        switch (text) {
            case "A":
                System.out.print("Alpha");
                break;
            case "B":
                System.out.print("Beta");
                break;
            default:
                System.out.print("Default");
        }
    }
}

What occurs when this code is executed?

A

It throws a NullPointerException at runtime before evaluating any case label

B

It fails to compile because null is not an accepted constant expression

C

It prints Default because null does not match "A" or "B"

D

It prints Alpha

Sections you finish are checked off in the contents.