Concepts / Switch Statements

Switch Statements

A switch expression computes and returns a single value, so it is used when the purpose of a switch is to produce a value for an assignment or another expression.

  • Programming

Watching a Switch Choose a Value

A switch is a way to choose between several possible outcomes based on one value. This article follows a traced run of a small program that chooses a value and stores it in a variable. As you read, keep two questions in mind: which case the switch chose, and what value ended up in the variable. Most of the rules in this article come from one observation: a switch expression is part of an assignment, so the value it computes is the value that gets stored.

What do you think happens?

Before reading on, what does the variable size hold after this code runs?

  • small

  • medium

  • large

  • Nothing, because the code does not compile

Reveal answer
Answer:

medium

The controlling expression count holds 4. The case labeled 3, 4 matches that value, so its result "medium" becomes the value of the switch expression, and the assignment stores it in size.

java
Output
medium
Notes on the code
  • count holds 4, so the entry with the label 3, 4 is chosen
  • the chosen entry's expression is the value of the whole switch
  • the assignment stores that value in size
  • the default entry would supply large for any value no earlier case matches

Expression Versus Statement

A switch statement tests one expression and jumps to the case label that matches its value. A switch expression also chooses a case, but it computes and returns a single value, so it is used when the purpose of the switch is to produce a value for an assignment or another expression. Many switch statements exist for one purpose: to assign a value to a variable, where the value depends on which case matched. The expression form handles that pattern more directly.

FeatureSwitch statementSwitch expression
Main purposePerforms actions by jumping to a positionComputes a single value
What each case holdsA statementAn expression or a throw statement
Can sit where a value is expectedNoYes, for example on the right side of an assignment or inside a print call
Must always produce a resultNot requiredYes, or it throws an exception, which is why a default case is usually included

Comparing a switch statement with a switch expression, based on the source's description of each form.

Reading the Switch Expression Template

Read the template from left to right. The variable on the left receives the result. The keyword switch is followed by the controlling expression in parentheses. Each case has a value, an arrow, and then an expression that gives the result for that case. The default case covers any value not matched by an earlier case. The closing brace is followed by the semicolon that ends the assignment.

java
switch (count)controlling expression1, 2case values"small"result expression for thatcase3, 4case values"medium"result expression for thatcasedefaultcovers unmatched values"large"result if no case matches
Which part of this switch expression is the case value, which is the result expression, and which is the default case?

In the anatomy above, the case values are the labels that the controlling expression is compared against. The result expressions are the values the switch produces when that case is chosen. The default entry is the fallback. The source explains that each case holds an expression rather than a statement, which is why the arrow form separates each case's value from the result it produces.

Yield and Blocks in a Switch Expression

Sometimes a case needs more than one step before it can produce its value. In that situation, the expression in the case can be replaced by a block containing several statements. The block itself does not produce the switch's value. Instead, the value is specified by a yield statement, such as yield 42. The value after the word yield is returned as the value of the switch expression. Only the case that was chosen runs, so only its yield determines the result.

case 3 blockchosen because day is 3label computedstatements inside the blockrunyield label+dayvalue after yieldswitch resultvalue of the whole switchnameassignment stores theresultrunsreaches yieldleaves the switchassigned
When a case has a block, where does the value passed to yield travel, and how does it become the result assigned to the variable?
Worked example

Following a yield through a block case

An int named code holds 2. A switch expression assigns its result to a String named text. Case 2 is a block that declares String part equal to "ab" and then yields part followed by "!". Find the value of text.

  1. 1
    Identify the controlling value

    code holds 2, so control moves to the entry for case 2.

  2. 2
    Run the block

    The block declares part and sets it to "ab". No value leaves the switch yet, because a block does not produce the result by itself.

  3. 3
    Find the yield

    The yield statement's value is part followed by "!", which is "ab!".

  4. 4
    Leave the switch

    The value after yield becomes the value of the switch expression, and no other case runs.

  5. 5
    Store the result

    The assignment stores "ab!" in text.

Result

text holds "ab!"

Finding the Case Behind an Unexpected Value

When a switch expression produces a surprising value, the useful question is which case was chosen and what that case produced. In the traced run, the divergence point is the case that was actually selected, not the variable that was assigned. The assignment only stores what the switch returned, so a wrong stored value usually traces back to a case expression or a yield value that was not the one you expected.

  1. 1
    Start with the controlling expression and evaluate it to a single value.
  2. 2
    Find the case whose label contains that value. Check every label, including comma-separated lists.
  3. 3
    If the chosen case is a single expression, that expression is the result.
  4. 4
    If the chosen case is a block, look for its yield statement and evaluate the value after yield.
  5. 5
    If no case matches, the default case supplies the result.

Switch expressions are not limited to assignment statements. Because they compute a value, you can use one anywhere another expression could be used, such as inside an output statement. The same tracing steps apply in that case: find the chosen entry, then find the value it produces.

java
Output
Other
Notes on the code
  • no variable holds the switch's value
  • day is 5, and no listed case matches it
  • the default case supplies Other, which is passed directly to println

Which Types a Switch Can Test

A switch statement only accepts expressions of certain types. The value of the expression can be one of the primitive integer types int, short, or byte. It can be the primitive char type. It can be String. It can also be an enum type, which is a type whose values are named constants. The expression may not be double or float.

The positions a switch can jump to are marked with case labels. A case label takes the form case followed by a constant list. The constant list consists of one or more literals of the same type as the expression in the switch, separated by commas. The label is followed by an arrow, made of a hyphen and a greater-than character, and then a single statement. That statement can be a subroutine call statement, a throw statement, or a block statement containing several nested statements. A switch can also have at most one default entry in place of a case label, and all case constants must be different.

Fall-Through in the Traditional Colon Form

Java has two syntaxes for switch statements. The original syntax was modeled on the C programming language, and the source describes it as error-prone and somewhat awkward to read. It is still common in existing code, so you need to be able to read it. In this form, the code under one label keeps running until a break is reached. That is why an unexpected result can start at a case label and continue into the next one.

java
Output
three
Notes on the code
  • control jumps to the case 2 label
  • word is set to two, but there is no break after it
  • execution continues into the code under case 3
  • the break after word = three is the first one reached
count = 2controlling valuematch case 2control jumps hereword = twono break after this lineword = threecode under case 3 runsbreakleaves the switchprintln wordprints threeevaluatedmatchedfalls throughreaches breakafter switch
After the selector matches a case, which lines run next, and at what point does execution skip or fall into another case?

Read the flowchart as the trace of the program. Control jumps directly to the case 2 label because count is 2. The line word = two runs, but nothing stops execution there. The code under case 3 runs next, so word becomes three. The first break is reached after that, and then println prints three. The divergence point is the case 2 label, where control arrives. The missing break after word = two is what lets execution continue.

Traditional Versus Java 17 Syntax

AspectTraditional colon syntaxJava 17 arrow syntax
Label formcase value: followed by statementscase value -> followed by one statement, expression, or block
Ending each caseA break is needed to stop fall-throughNo break is needed, because the arrow selects one entry
AvailabilityOriginal syntax, still common in existing codeRequires Java 17
Source's adviceRead it, because much existing code uses itUse it when writing new code

Traditional and Java 17 switch syntax, as described in the source.

java

This article follows the new syntax first, and the traditional form is treated later. Both forms rest on the same idea: a value selects a jump point. Learning the case label rules carefully therefore helps with either form.

Mistakes Beginners Make With Switches

  • Common mistake

    Expecting a switch expression to finish without a value

    Why it fails

    A switch expression must always compute a value or throw an exception, so it usually includes a default case.

    Do this instead

    Add a default entry that supplies a value for any input no other case matches.

  • Common mistake

    Thinking a block in a case produces the value

    What it looks like
    case 2 -> { String part = "ab"; part + "!"; }
    Why it fails

    The block does not produce the switch's value. The value comes from a yield statement.

    Do this instead

    Use yield part + "!"; inside the block so that the value after yield leaves the switch.

  • Common mistake

    Forgetting break in the colon form

    What it looks like
    case 2: word = "two"; case 3: word = "three"; break;
    Why it fails

    Without a break, execution continues into the code under the next label.

    Do this instead

    Place a break after each case that should stop there, or switch to the arrow form in new code.

  • Common mistake

    Using a label whose literal type does not match the selector

    Why it fails

    Case labels must be literals of the same type as the switch expression, and all case constants must be different.

    Do this instead

    Check the selector type first, then write labels of that type and make sure no two are equal.

  • Common mistake

    Using double or float as the selector

Practice Tracing a Switch

MEDIUM

An int named day holds 3. A switch expression with cases for 1, 2, and 3 assigns its result to a String named name. The case for 3 is a block that declares a local String named label, then yields label joined with the text day. Trace the switch and state which case was chosen, which line produces the value, and what the variable name holds after the assignment.

Hints
  • Start with the controlling value 3 and find the matching case.
  • The block does not give the result by itself. Look for the yield statement.
  • The value after yield leaves the switch and is stored by the assignment.
EASY

A colon-form switch has cases 1, 2, and 3, and the code under case 1 sets a value without a break. If the selector holds 1, which case bodies run, and where does execution stop?

Hints
  • Control jumps to the case 1 label.
  • Without a break, execution falls through into the next label.

Key Takeaways

  • A switch expression computes and returns a single value, so it is used when a switch must produce a value for an assignment or another expression.
  • Each case in an arrow-form switch expression holds an expression or throw statement, and a case with a block gives its value through a yield statement.
  • A switch selector may be int, short, byte, char, String, or an enum type, and case labels must use literals of the same type with no duplicates.
  • When tracing a surprising result, find the chosen case first, then follow its expression or yield, and in the colon form watch for a missing break that causes fall-through.