Java Switch

Su Suriya Ravichandran Updated 14 Nov 2026
7 min read ·Lesson 18 of 133

A switch selects one branch by comparing a single value against a list of cases. Where an if/else if chain tests a series of arbitrary conditions, switch answers one question: which of these known values is it? Java has two forms: the traditional colon-and-break form inherited from C, and the arrow form introduced in Java 14, which can also produce a value.

Arrow Form (Java 14+)

int day = 3;

switch (day) {
    case 1 -> System.out.println("Monday");
    case 2 -> System.out.println("Tuesday");
    case 3 -> System.out.println("Wednesday");
    case 4 -> System.out.println("Thursday");
    case 5 -> System.out.println("Friday");
    case 6, 7 -> System.out.println("Weekend");
    default -> System.out.println("Invalid day");
}

Output:

Wednesday

How it works:

  1. The expression in parentheses (day) is evaluated once.
  2. Its value is compared against each case label.
  3. The matching case's code runs, then the switch ends. No other case runs.
  4. If nothing matches, default runs. Without a default, nothing happens.

case 6, 7 lists several values that share one branch. A branch with more than one statement uses a block:

case 6, 7 -> {
    System.out.println("Weekend");
    planTrip();
}

Switch Expressions

The arrow form can produce a value, so a whole switch can sit on the right side of an assignment, in a return, or as a method argument.

int month = 2;
int year = 2028;

int days = switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> 31;
    case 4, 6, 9, 11 -> 30;
    case 2 -> (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ? 29 : 28;
    default -> throw new IllegalArgumentException("Invalid month: " + month);
};

System.out.println(days);   // 29

A switch expression must be exhaustive: it has to produce a value for every possible input. For an int or String that means a default is required. A branch may also throw instead of producing a value, as the default does here.

The semicolon after the closing brace belongs to the assignment statement, not the switch.

yield

When a branch in a switch expression needs several statements, it uses a block and yield to supply the value:

String level = switch (score / 10) {
    case 10, 9 -> "Excellent";
    case 8, 7 -> "Good";
    case 6 -> {
        log("Borderline score: " + score);
        yield "Average";
    }
    default -> "Needs improvement";
};

yield is to a switch expression what return is to a method: it ends the branch and provides the result. return cannot be used for this, because it would leave the enclosing method.

Traditional Form (Colon and break)

Older code, and code that must compile on Java 13 or earlier, uses colons:

int day = 3;

switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    default:
        System.out.println("Other day");
}

Fall-through

In the colon form, a matching case runs its statements and then continues into every following case until it reaches a break or the end of the switch. This is called fall-through.

int day = 2;

switch (day) {
    case 1:
        System.out.println("Monday");
    case 2:
        System.out.println("Tuesday");
    case 3:
        System.out.println("Wednesday");
    default:
        System.out.println("Other day");
}

Output:

Tuesday
Wednesday
Other day

The match at case 2 starts execution, and without any break it runs straight to the end. Forgetting a break is the classic switch bug, and it is why the arrow form was added: arrow cases never fall through.

Fall-through is occasionally intentional, to group labels:

switch (ch) {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        vowels++;
        break;
    default:
        consonants++;
}

In the arrow form the same grouping is written case 'a', 'e', 'i', 'o', 'u' -> vowels++;.

The two forms cannot be mixed in a single switch: every case uses -> or every case uses :.

Which Types Can Be Switched On

TypeSince
byte, short, char, int and their wrappersJava 1.0 / 5
enumJava 5
StringJava 7
Any reference type, using type patternsJava 21

long, float, double, and boolean cannot be switched on with constant labels. Use if/else for those.

Case labels must be compile-time constants (literals, final constants, or enum constants), and each may appear only once.

Switching on strings

String command = "stop";

switch (command) {
    case "start" -> startEngine();
    case "stop" -> stopEngine();
    case "status" -> printStatus();
    default -> System.out.println("Unknown command: " + command);
}

String matching uses equals, so it is case-sensitive: "Stop" matches only the default. Normalize first with command.toLowerCase() if case should not matter. Switching on a null string throws NullPointerException unless a case null label is present (Java 21+).

Switching on enums

enum Status { PENDING, SHIPPED, DELIVERED, CANCELLED }

Status s = Status.SHIPPED;

String message = switch (s) {
    case PENDING -> "Preparing your order";
    case SHIPPED -> "On the way";
    case DELIVERED -> "Delivered";
    case CANCELLED -> "Order cancelled";
};

Inside the switch, enum constants are written without the type prefix (SHIPPED, not Status.SHIPPED). This switch expression has no default and still compiles, because it covers every constant of the enum, so the compiler knows it is exhaustive. If a new constant such as RETURNED is later added, this switch stops compiling until the new case is handled. Omitting default on enum switch expressions turns that change into a compile error rather than a silent gap.

Pattern Matching in switch (Java 21+)

A switch can test the type of an object and bind a variable:

static String describe(Object obj) {
    return switch (obj) {
        case null -> "null";
        case Integer i when i < 0 -> "negative integer " + i;
        case Integer i -> "integer " + i;
        case String s when s.isBlank() -> "blank string";
        case String s -> "string of length " + s.length();
        case int[] arr -> "int array of " + arr.length;
        default -> "something else: " + obj.getClass().getSimpleName();
    };
}
describe(-5);        // negative integer -5
describe(42);        // integer 42
describe("   ");     // blank string
describe("hi");      // string of length 2
describe(3.5);       // something else: Double

case Integer i matches when obj is an Integer and binds it to i, already cast. A when clause (a guard) adds a condition that must also hold. Cases are checked top to bottom, so the more specific guarded case must come before the general one; the compiler rejects a case that an earlier case fully covers.

Combined with sealed interfaces and records, this allows exhaustive switches over a closed set of types, covered in the records and sealed classes lessons.

switch Versus if/else

SituationBetter choice
One variable compared against several fixed valuesswitch
Ranges (score >= 90)if/else if
Conditions involving different variablesif/else if
Choosing a value from a known setswitch expression
Branching on the type of an objectpattern switch
long, double, boolean valuesif/else

A switch makes the "one value, several known options" structure visible, and with enums and sealed types the compiler can verify that every option is handled.

Practical Example: Command Menu

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class TodoMenu {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        List<String> tasks = new ArrayList<>();
        boolean running = true;

        while (running) {
            System.out.print("\n[a]dd  [l]ist  [d]elete  [q]uit > ");
            String choice = sc.nextLine().strip().toLowerCase();

            switch (choice) {
                case "a", "add" -> {
                    System.out.print("Task: ");
                    tasks.add(sc.nextLine().strip());
                }
                case "l", "list" -> {
                    if (tasks.isEmpty()) {
                        System.out.println("No tasks.");
                    }
                    for (int i = 0; i < tasks.size(); i++) {
                        System.out.println((i + 1) + ". " + tasks.get(i));
                    }
                }
                case "d", "delete" -> {
                    System.out.print("Number to delete: ");
                    int n = Integer.parseInt(sc.nextLine().strip());
                    if (n >= 1 && n <= tasks.size()) {
                        System.out.println("Removed: " + tasks.remove(n - 1));
                    } else {
                        System.out.println("No such task.");
                    }
                }
                case "q", "quit" -> running = false;
                default -> System.out.println("Unknown option: " + choice);
            }
        }
    }
}

The input is normalized with strip().toLowerCase() so that "A" and " add " both match. Each case accepts a short and a long command. Multi-statement branches use blocks, and because the arrow form never falls through, no break statements are needed. Setting running = false ends the surrounding while loop. The delete branch would throw on non-numeric input; production code would validate it as shown in the User Input lesson.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Java Switch and get answers drawn from it.

Signed-in readers only.