PrepZone Logo
PrepZone

Switch Expressions and Text Blocks

Switch that returns a value with no fall-through, plus multi-line strings that stay readable.

Read these first

Why this matters

  • Missing break is one of the oldest bug sources in C-family languages, and switch expressions remove the possibility entirely.
  • Exhaustiveness means adding an enum constant breaks the build rather than silently falling into default.
  • Embedded JSON, SQL and HTML are everywhere, and escaped single-line strings make them unreadable and error-prone.

The old switch statement

Java
String season;
switch (month) {
    case DECEMBER:
    case JANUARY:
    case FEBRUARY:
        season = "winter";
        break;                     // forget this and you silently get "spring"
    case MARCH:
    case APRIL:
    case MAY:
        season = "spring";
        break;
    default:
        season = "unknown";
}

Four problems: the variable must be declared outside and uninitialised, each case needs an explicit break, each grouped label needs its own line, and the whole thing is a statement so it cannot be used inline.

Switch expressions

Java
String season = switch (month) {
    case DECEMBER, JANUARY, FEBRUARY -> "winter";
    case MARCH, APRIL, MAY -> "spring";
    case JUNE, JULY, AUGUST -> "summer";
    case SEPTEMBER, OCTOBER, NOVEMBER -> "autumn";
};
Old switch statement
case + breakForget break and it falls through
Assigns to a variableCannot be final
Switch expression
case A -> valueNo fall-through
Returns a valueAssign directly to a final variable
ExhaustiveCompiler checks every case
Arrow labels do not fall through and the switch produces a value. Over an enum or sealed type the compiler also checks that every case is covered.

What the arrow form changes

  • No fall-through. Each arm runs only its own branch, so break is gone.
  • Multiple labels per arm, comma-separated.
  • It is an expression, so it can be assigned, returned, or passed as an argument.
  • Exhaustiveness is required when it produces a value. Over an enum, covering every constant is enough and default is unnecessary.
Java
// Returned directly
int days(Month month) {
    return switch (month) {
        case FEBRUARY -> 28;
        case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
        default -> 31;
    };
}

// Used inline
log.info("handling a {} request", switch (method) {
    case GET, HEAD -> "read";
    case POST, PUT, PATCH, DELETE -> "write";
});

Multi-line arms with yield

When an arm needs more than one statement, use a block and yield the value.

Java
int score = switch (grade) {
    case "A" -> 4;
    case "B" -> 3;
    case "C" -> {
        audit.record("borderline grade");
        yield 2;                                 // yield, not return
    }
    default -> throw new IllegalArgumentException("unknown grade: " + grade);
};

The colon form still exists

The old syntax also works as an expression, using yield instead of assignment. It is legal but rarely worth it.

Java
int value = switch (code) {
    case 1: yield 10;
    case 2: yield 20;
    default: yield 0;
};
AspectArrow formColon form
Fall-throughImpossibleDefault behaviour — needs break
ScopeEach arm has its own scopeAll cases share one scope
ValueThe expression after -> , or yield in a blockyield only
Grouping labelscase A, B, C ->Stacked case labels
RecommendationUse thisOnly when maintaining old code
  • Fall-through

    Arrow formImpossible
    Colon formDefault behaviour — needs break
  • Scope

    Arrow formEach arm has its own scope
    Colon formAll cases share one scope
  • Value

    Arrow formThe expression after -> , or yield in a block
    Colon formyield only
  • Grouping labels

    Arrow formcase A, B, C ->
    Colon formStacked case labels
  • Recommendation

    Arrow formUse this
    Colon formOnly when maintaining old code

The shared scope in the colon form is a real hazard: a variable declared in one case is visible in the next.

Switch on what

Java
switch (intValue) { }          // int, short, char, byte and their wrappers
switch (enumValue) { }         // enum — exhaustiveness checked
switch (stringValue) { }       // String, since Java 7
switch (object) { }            // any type, with type patterns — Java 21

Patterns in switch are covered separately; the arrow form and exhaustiveness described here are what make them possible.

Text blocks

Java
// Escaped — the structure is invisible
String json = "{\n  \"id\": 42,\n  \"name\": \"Ada\",\n  \"tags\": [\"a\", \"b\"]\n}";

// Text block — the structure is the structure
String json = """
        {
          "id": 42,
          "name": "Ada",
          "tags": ["a", "b"]
        }
        """;

A text block starts with """ followed by a line terminator, and ends with """. The result is a perfectly ordinary String — there is no new type, and interning and equals work exactly as before.

Incidental indentation

This is the rule that matters. The compiler finds the minimum indentation across all non-blank lines and the closing delimiter line, then strips that much from every line.

Java
String sql = """
        SELECT id, name
          FROM users
         WHERE active = true
        """;
// Produces:
// SELECT id, name
//   FROM users
//  WHERE active = true

The eight leading spaces are incidental and removed; the relative indentation of FROM and WHERE is essential and kept.

Java
// Moving the closing delimiter left preserves more indentation
String indented = """
        line one
        line two
""";
// Produces "        line one\n        line two\n" — the delimiter at column 0 strips nothing

Line endings and escapes

Java
// A trailing \ suppresses the newline — useful for long single-line text
String oneLine = """
        this is all \
        one single line\
        """;
// "this is all one single line"

// \s preserves trailing whitespace
String padded = """
        value   \s
        """;

// Quotes need no escaping
String quoted = """
        He said "hello" and left.
        """;

Where text blocks earn their place

Java
String query = """
        SELECT o.id, o.total, c.name
          FROM orders o
          JOIN customers c ON c.id = o.customer_id
         WHERE o.created_at >= ?
           AND o.status = 'OPEN'
         ORDER BY o.created_at DESC
        """;

String template = """
        <html>
          <body>
            <h1>%s</h1>
            <p>%s</p>
          </body>
        </html>
        """;
String page = template.formatted(title, body);     // formatted() reads better than String.format here

Common misreadings

  • "Switch expressions can fall through." The arrow form cannot. Only the colon form falls through.
  • "default is always required." Not when the switch covers every enum constant, or every permitted subtype of a sealed type.
  • "yield and return are the same." yield gives a value to the switch; return exits the method and is illegal in a switch expression.
  • "The colon form is deprecated." It still compiles; it is just worse.
  • "A text block is a new kind of string." It is a String literal with different source syntax.
  • "All leading whitespace is kept." Common indentation is stripped, including based on the closing delimiter's position.
  • "Text blocks support interpolation." They do not. Use formatted().
  • "Line endings follow the platform." Always \n.

Quick recall

Everything you need if you only revisit this box.

  • The arrow form removes fall-through, allows comma-separated labels, and makes switch an expression.
  • A switch expression must be exhaustive — over an enum that means every constant, and then default is unnecessary and adding a constant breaks the build.
  • Use a block with yield for multi-statement arms; return is illegal, and a throw arm produces no value.
  • The colon form shares one scope across cases, which is a hazard; prefer arrows.
  • A text block opens with """ plus a newline and produces an ordinary String.
  • Incidental indentation is stripped, determined by the least-indented non-blank line and the closing delimiter — so position the closing """ to control it.
  • \ at end of line joins lines; \s preserves a trailing space; quotes need no escaping; line endings are always \n.
  • No interpolation — use formatted().

Test yourself

Answer these before moving on — recall is what makes it stick.