java-foundations Lesson 13 16 min read

Switch Statements and the Ternary Operator

Cleaner alternatives for multi-branch decisions

Reading: Reges & Stepp: Ch. 4 §4.4–4.5

After this lesson, you will be able to:

  • Write switch statements using both traditional (case:/break) and modern arrow (->) syntax
  • Identify and avoid fall-through bugs in traditional switch statements
  • Use switch expressions to assign values directly to variables
  • Apply the ternary operator for simple conditional assignments
  • Decide when to use switch, if-else-if, or the ternary operator

Press 1 for English, 2 for Spanish…

You have written if-else-if chains. They work. But picture a phone menu: “Press 1 for English, 2 for Spanish, 3 for French, 4 for German, 5 for Mandarin.” An if-else-if chain checking the same variable against five exact values gets tedious fast. There is a better tool for this job: the switch statement.

And sometimes you just need a quick one-liner: “If the user is 18 or older, set the label to Adult; otherwise, Minor.” A full if-else block feels heavy for that. The ternary operator handles it in a single expression.

This lesson covers both tools, when to reach for them, and when to stick with if-else.


The Traditional Switch Statement

The traditional switch tests one variable against a list of exact values:

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;
    case 4:
        System.out.println("Thursday");
        break;
    case 5:
        System.out.println("Friday");
        break;
    default:
        System.out.println("Weekend or invalid");
}
// Output: Wednesday

Here is how it works:

  1. Java evaluates the expression in parentheses (day) once.
  2. It compares the result against each case label, top to bottom.
  3. When it finds a match, it executes the code after that label.
  4. break exits the switch. Without it, execution keeps going into the next case.
  5. default handles any value that does not match.

Key Insight: Each case label must be a compile-time constant — a literal number, character, or string. You cannot use variables or method calls as case labels.


Fall-Through: The Classic Trap

Forget a break and Java silently continues into the next case. This is called fall-through, and it is the number one switch bug:

int day = 2;

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

When day is 2, Java matches case 2 and prints “Tuesday.” But there is no break, so it falls through to case 3 and prints “Wednesday” before finally hitting break.

Common Pitfall: Missing break is inherited from C (1972) and was originally considered a feature. In practice, it causes far more bugs than it solves. Always include break in traditional switch unless you deliberately want fall-through.

Intentional Fall-Through: Grouping Cases

The one legitimate use of fall-through is grouping cases that share the same code:

int month = 3;
int days;

switch (month) {
    case 1: case 3: case 5: case 7:
    case 8: case 10: case 12:
        days = 31;
        break;
    case 4: case 6: case 9: case 11:
        days = 30;
        break;
    case 2:
        days = 28; // ignoring leap years
        break;
    default:
        days = -1; // invalid month
}

Months 1, 3, 5, 7, 8, 10, and 12 all fall through to days = 31. This works, but it is still easy to misread. Modern syntax solves this more cleanly.


Check Your Understanding

What does this code print?

int x = 1;
switch (x) {
    case 1:
        System.out.print("A");
    case 2:
        System.out.print("B");
        break;
    case 3:
        System.out.print("C");
}

Modern Arrow Syntax (Java 14+)

Modern Java replaces the colon-break pattern with arrow syntax (->):

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");
    default -> System.out.println("Weekend or invalid");
}
// Output: Wednesday

The arrow syntax has three advantages over the traditional form:

  • No fall-through. Each case is independent. No break needed.
  • Cleaner. Less boilerplate, easier to scan.
  • Safer. The most common switch bug is eliminated entirely.

The Trick: Use arrow syntax for all new code in this course. You need to recognize the traditional syntax because it appears in textbooks, older projects, and exam questions. But when you write new switch statements, use ->.

Grouping Cases with Commas

Multiple values can share a single arrow case:

int month = 3;

int days = switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> 31;
    case 4, 6, 9, 11            -> 30;
    case 2                       -> 28;
    default                      -> -1;
};

Compare this to the traditional fall-through version above. The comma-separated cases are dramatically cleaner.


Switch Expressions: Returning Values

With arrow syntax, a switch can be an expression that produces a value:

int day = 5;

String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4 -> "Thursday";
    case 5 -> "Friday";
    case 6 -> "Saturday";
    case 7 -> "Sunday";
    default -> "Invalid day";
};

System.out.println(dayName); // Friday

Notice the semicolon after the closing brace. The entire switch is an expression assigned to dayName, so it ends with ; like any other assignment statement.

Multi-Statement Cases with yield

When a case needs more than one line, use braces and the yield keyword to return a value:

String category = switch (score / 10) {
    case 10, 9 -> {
        System.out.println("Excellent work!");
        yield "A";
    }
    case 8 -> {
        System.out.println("Good job!");
        yield "B";
    }
    default -> {
        System.out.println("Keep trying!");
        yield "Below B";
    }
};

Key Insight: yield exits the switch expression and provides its value. return exits the entire method. Do not confuse them. Use yield inside switch expressions; use return to exit methods.


What Types Can You Switch On?

Type Allowed? Notes
int, byte, short, char Yes The original supported types
String Yes Since Java 7 (uses .equals() internally)
enum Yes Type-safe; compiler checks all cases
double, float No Floating-point comparison is unreliable
boolean No Only two values — use if-else
long No Too many possible values

Common Pitfall: String matching in switch is case-sensitive. "Medium" does not match case "medium". Normalize input before switching: String size = userInput.toLowerCase();


When to Use Switch vs. If-Else-If

Situation Best choice Why
Exact values (1, 2, 3, …) switch Cleaner, potentially faster
Ranges (0–59, 60–69, …) if-else-if Switch cannot handle ranges
Complex boolean logic (&&, ||) if-else Switch cannot express compound conditions
String matching against known values switch Clean since Java 7
Only two paths if-else Switch is overkill

The Trick: Ask yourself: “Am I testing one variable against specific values?” If yes, use switch. If you are testing ranges, combining conditions, or comparing multiple variables, use if-else.


Check Your Understanding

Which scenario is best suited for a switch statement?


From CSCD 110: Python 3.10 introduced match-case, which is conceptually similar to Java’s switch:

Python 3.10+ Java (arrow syntax)
match day: switch (day) {
` case 1: | case 1 ->`  
` case _: | default ->`  

If you used match-case in CSCD 110, the idea transfers directly. If you used if-elif chains instead, think of switch as a specialized shorthand for checking one variable against many exact values.


Complete Example: Day-of-Week Planner

Putting it together, here is a program that reads a day number and prints the day name along with a suggested activity:

import java.util.Scanner;

void main() {
    Scanner scanner = new Scanner(System.in);

    System.out.print("Enter day number (1-7): ");
    int day = scanner.nextInt();

    String dayName = switch (day) {
        case 1 -> "Monday";
        case 2 -> "Tuesday";
        case 3 -> "Wednesday";
        case 4 -> "Thursday";
        case 5 -> "Friday";
        case 6 -> "Saturday";
        case 7 -> "Sunday";
        default -> "Invalid";
    };

    String activity = switch (day) {
        case 1, 3, 5 -> "Lecture day -- review notes before class.";
        case 2, 4    -> "Lab day -- start early, ask questions.";
        case 6       -> "Catch up on reading and practice problems.";
        case 7       -> "Rest. Seriously. You need it.";
        default      -> "Not a valid day number.";
    };

    if (!"Invalid".equals(dayName)) {
        System.out.println(dayName + ": " + activity);
    } else {
        System.out.println("Please enter a number between 1 and 7.");
    }
}

This uses two switch expressions: one to look up the day name, one to look up the activity. Both use arrow syntax with comma-grouped cases. The default branch handles invalid input.


The Ternary Operator

The ternary operator is a one-line if-else expression:

condition ? valueIfTrue : valueIfFalse
int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // Adult

This is exactly equivalent to:

String status;
if (age >= 18) {
    status = "Adult";
} else {
    status = "Minor";
}

The ternary version is shorter and often clearer for simple assignments.

Good Uses

// Maximum of two values
int max = (a > b) ? a : b;

// Singular vs. plural label
String label = (count == 1) ? "item" : "items";
System.out.println(count + " " + label);

// Absolute value (manual)
int abs = (x >= 0) ? x : -x;

Each of these is a straightforward, one-value assignment. The ternary reads naturally.

When NOT to Use the Ternary

// BAD: nested ternary is unreadable
String result = (x > 0) ? (y > 0) ? "Both+" : "Only x+" : "x-";

// GOOD: use if-else for anything complex
String result;
if (x > 0 && y > 0) {
    result = "Both+";
} else if (x > 0) {
    result = "Only x+";
} else {
    result = "x-";
}

Key Insight: Use the ternary operator for simple, one-value assignments. If the condition or the values are complex, or if you need to do more than assign a single value, use if-else. The goal is readability, not brevity. Nested ternaries are a code smell that make debugging miserable.


Complete Example: Simple Calculator

This program combines switch expressions, the ternary operator, and input handling:

import java.util.Scanner;

void main() {
    Scanner scanner = new Scanner(System.in);

    System.out.print("Enter first number: ");
    double a = scanner.nextDouble();
    System.out.print("Enter operator (+, -, *, /): ");
    String op = scanner.next();
    System.out.print("Enter second number: ");
    double b = scanner.nextDouble();

    // Validate division by zero
    if (op.equals("/") && b == 0) {
        System.out.println("Error: cannot divide by zero");
        return;
    }

    String result = switch (op) {
        case "+" -> a + " + " + b + " = " + (a + b);
        case "-" -> a + " - " + b + " = " + (a - b);
        case "*" -> a + " * " + b + " = " + (a * b);
        case "/" -> a + " / " + b + " = " + (a / b);
        default  -> "Unknown operator: " + op;
    };

    // Ternary to choose output label
    String label = ("Unknown operator: ".equals(result.substring(0, Math.min(19, result.length()))))
        ? "Error"
        : "Result";

    System.out.println(label + ": " + result);
}

The switch expression maps each operator to a formatted result string. Division by zero is validated before the switch. The default case handles invalid operators.


Check Your Understanding

Which use of the ternary operator is appropriate?


Check Your Understanding

What is wrong with this code?

String size = "medium";
double price = switch (size) {
    case "small"  -> 2.00;
    case "medium" -> 3.00;
    case "large"  -> 4.00;
}

Decision Tool Summary

You now have four tools for making decisions in Java. Here is when to reach for each one:

Tool Best for Avoid when
if-else Two paths, range checks, compound conditions You are checking one variable against many exact values
if-else-if Ordered ranges, complex boolean logic A switch would be cleaner
switch (arrow) One variable, multiple exact values You need ranges or compound conditions
Ternary ? : Simple one-value assignments The logic is complex or nested

Big Picture: Switch statements appear throughout real-world programming: menu systems, command dispatchers, protocol handlers. In CSCD 211, you will use switch with enums to represent object states. In CSCD 260 (Computer Organization), the switch concept maps directly to a jump table — a hardware-optimized lookup that the CPU uses to dispatch operations in constant time.


Summary

Switch statements replace long if-else-if chains when you are testing one variable against exact values. The traditional syntax (case:/break) requires break to prevent fall-through — the most common switch bug. Modern arrow syntax (->) eliminates fall-through entirely and can return values as a switch expression. Use arrow syntax for all new code.

The ternary operator (condition ? a : b) is a one-line if-else expression for simple assignments. It is not a replacement for if-else — it is a shortcut for the narrow case where you need to pick one of two values based on a condition. Never nest ternaries.

Next up: Loopswhile, for, and do-while. Loops combined with conditionals let you retry invalid input, process collections of data, and build interactive programs that run until the user says “quit.”