[ Previous Section | Next Section | Chapter Index | Main Index ]

Section 3.6

The switch Statement


The second branching statement in Java is the switch statement, which is introduced in this section. The switch statement is used much less often than the if statement, but it is sometimes useful for expressing a certain type of multiway branch.


3.6.1  The Basic switch Statement

A switch statement allows you to test the value of an expression and, depending on that value, to jump directly to some location within the switch statement. Only expressions of certain types can be used. 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. Or it can be an enum type (see Subsection 2.3.5 for an introduction to enum types). In particular, note that the expression cannot be a double or float value.

Java has two different syntaxes for switch statements. The original switch syntax, which like other Java control structures was modeled on the C programming language, is error-prone and kind-of ugly. The new syntax, which requires Java 17, is an improvement. You still need to know the traditional syntax, since it is used in a lot of existing code, but my advice would be to use the new syntax when you write new code. We look at the new syntax first, and will cover the traditional syntax at the end of this section.

The positions within a switch statement to which it can jump are marked with case labels that take the form: "case constantList". The constantList here consists of one or more literals of the same type as the expression in the switch, separated by commas. For example:

       case 2, 4, 8
or 
       case "Paper"

The case label is followed by ->, that is by a symbol made up of a hyphen and a greater-than character, and then by a single statement. The statement can be a subroutine call statement, a throw statement, or a block statement, containing several nested statements. A switch statement can also, optionally, have one jump point labeled with default instead of with a case label. The syntax for the statement can be specified as follows, noting that there can be at most one default case and that all the constants in the case labels must be different:

switch ( expression ) {
    case-label-or-default -> statement 
    case-label-or-default -> statement 
        .
        .
        .
    case-label-or-default -> statement
}

When the computer executes this switch statement, it evaluates the expression. If the value is one of the constants in a case label, the computer executes the statement that follows that case label, and then jumps out of the switch statement. If the value of the expression does not match any of the case constants, then the computer looks for a default case, and if one is present, executes the statement that follows it.

It is probably easiest to look at an example. This is not a useful example, but it should be easy to follow:

switch ( N ) {   // (Assume N is an integer variable.)
   case 1 -> System.out.println("The number is 1.");
   case 2, 4, 8 -> {
      System.out.println("The number is 2, 4, or 8.");
      System.out.println("(That's a power of 2!)");
   }
   case 3, 6, 9 -> {
      System.out.println("The number is 3, 6, or 9.");
      System.out.println("(That's a multiple of 3!)");
   }
   case 5 -> System.out.println("The number is 5.");
   default ->
      System.out.println("The number is 7 or is outside the range 1 to 9.");
}

The braces, { and }, in this example are required to group multiple statements into a single block statement. Braces could also be added to the other cases, but are not required there. This switch statement has exactly the same effect as the following multiway if statement:

if ( N == 1 ) {
    System.out.println("The number is 1.");
}
else if ( N == 2 || N == 4 || N == 8 ) {
    System.out.println("The number is 2, 4, or 8.");
    System.out.println("(That's a power of 2!)");
}
else if ( N == 3 || N == 6 || N == 9 ) {
    System.out.println("The number is 3, 6, or 9.");
    System.out.println("(That's a multiple of 3!)");
}
else if ( N == 5 ) {
    System.out.println("The number is 5.");
}
else {
    System.out.println("The number is 7 or is outside the range 1 to 9.");
}

More generally, any switch statement could be replaced by a multiway if statement. The switch statement can be easier to read. And it might be more efficient since the computer can jump directly to the correct case instead of working through a series of tests to get to the correct case.


3.6.2  Menus and switch Statements

One application of switch statements is in processing menus. A menu is a list of options. The user selects one of the options. The computer has to respond to each possible choice in a different way. If the options are numbered 1, 2, ..., then the number of the chosen option can be used in a switch statement to select the proper response.

In a command-line program, the menu can be presented as a numbered list of options, and the user can choose an option by typing in its number. It can be convenient to use a text block (see Subsection 2.3.4) to present the menu. Here is an example that could be used in a variation of the LengthConverter example from the previous section:

int optionNumber;   // Option number from menu, selected by user.
double measurement; // A numerical measurement, input by the user.
                    //    The unit of measurement depends on which
                    //    option the user has selected.
double inches;      // The same measurement, converted into inches.

/* Display menu of options, and get user's selected option number. */

System.out.println("""
        What unit of measurement does your input use?
        
                1. inches
                2. feet
                3. yards
                4. miles
                
        Enter the number of your choice:
        """);

optionNumber = TextIO.getlnInt();

/* Read user's measurement and convert to inches. */

switch ( optionNumber ) {
   case 1 -> {
       System.out.println("Enter the number of inches: ");
       measurement = TextIO.getlnDouble();
       inches = measurement;
   }
   case 2 -> {
       System.out.println("Enter the number of feet: ");
       measurement = TextIO.getlnDouble();
       inches = measurement * 12;
   }
   case 3 -> {
       System.out.println("Enter the number of yards: ");
       measurement = TextIO.getlnDouble();
       inches = measurement * 36;
   }
   case 4 -> {
       System.out.println("Enter the number of miles: ");
       measurement = TextIO.getlnDouble();
       inches = measurement * 12 * 5280;
    }
   default -> {
       System.out.println("Error!  Illegal option number!  I quit!");
       System.exit(1);
   }

} // end switch

/* Now go on to convert inches to feet, yards, and miles... */

Alternatively, this example could be designed to ask the use to enter the unit of measure as a string, instead of as an option number, and then use that string directly in a switch statement:

String units;       // Unit of measurement, entered by user.
double measurement; // A numerical measurement, input by the user.
double inches;      // The same measurement, converted into inches.

/* Read the user's unit of measurement. */

System.out.println("What unit of measurement does your input use?");
units = TextIO.getln().toLowerCase();

/* Read user's measurement and convert to inches. */

System.out.print("Enter the number of " + units + ":  ");
measurement = TextIO.getlnDouble();

switch ( units ) {
   case "inch", "inches", "in" ->  inches = measurement;
   case "foot", "feet", "ft"   ->  inches = measurement * 12;
   case "yard", "yards", "yd"  ->  inches = measurement * 36;
   case "mile", "miles", "mi"  ->  inches = measurement * 12 * 5280;
   default -> {
       System.out.println("Wait a minute!  Illegal unit of measure!  I quit!");
       System.exit(1);
   }
} // end switch

3.6.3  Enums in switch Statements

The type of the expression in a switch can be an enum type. In that case, the constants in the case labels must be values from the enum type. For example, suppose that the type of the expression is the enumerated type Season defined by

enum Season { SPRING, SUMMER, FALL, WINTER }

and that the expression in a switch statement is an expression of type Season. The constants in the case label must be chosen from among the values Season.SPRING, Season.SUMMER, Season.FALL, or Season.WINTER. However, there is a quirk in the syntax: when an enum constant is used in a case label, only the simple name, such as "SPRING" is used, not the full name, such as "Season.SPRING". Of course, the computer already knows that the value in the case label must belong to the enumerated type, since it can tell that from the type of expression used, so there is really no need to specify the type name in the constant. For example, assuming that currentSeason is a variable of type Season, then we could have the switch statement:

System.out.print("The months in " + currentSeason + " are: ");

switch ( currentSeason ) {
   case WINTER ->   // ( NOT Season.WINTER ! )
      System.out.println("December, January, February");
   case SPRING ->
      System.out.println("March, April, May");
   case SUMMER ->
      System.out.println("June, July, August");
   case FALL ->
      System.out.println("September, October, November");
}

3.6.4  Definite Assignment and switch Statements

As a somewhat more realistic example, the following switch statement makes a random choice among three possible alternatives. Recall that the value of the expression (int)(3*Math.random()) is one of the integers 0, 1, or 2, selected at random with equal probability, so the switch statement below will assign one of the values "Rock", "Paper", "Scissors" to computerMove, with probability 1/3 for each case:

switch ( (int)(3*Math.random()) ) {
   case 0 -> computerMove = "Rock";
   case 1 -> computerMove = "Paper";
   case 2 -> computerMove = "Scissors";
}

This switch statement is perfectly OK, but suppose that we use it in the following code segment:

String computerMove;
switch ( (int)(3*Math.random()) ) {
   case 0 -> computerMove = "Rock";
   case 1 -> computerMove = "Paper";
   case 2 -> computerMove = "Scissors";
}
System.out.println("The computer's move is " + computerMove);  // ERROR!

Now there is a subtle error on the last line! The problem here is due to definite assignment, the idea that the Java compiler must be able to determine that a variable has definitely been assigned a value before its value is used. Definite assignment was introduced in Subsection 3.1.5. In this example, it's true that the three cases in the switch cover all the possibilities, but the compiler is not smart enough to figure that out; it just sees that there is an integer-valued expression in the switch but not all possible integer values are covered by the given cases.

A simple solution is to replace the final case in the switch statement with default. With a default case, all possible values of the expression in the switch are certainly covered, and the compiler knows that computerMove is definitely assigned a value:

String computerMove;
switch ( (int)(3*Math.random()) ) {
   case 0 -> computerMove = "Rock";
   case 1 -> computerMove = "Paper";
   default -> computerMove = "Scissors";
}
System.out.println("The computer's move is " + computerMove);  // OK!

3.6.5  Switch Expressions

Often, the whole purpose of a switch statement is to assign a value to a variable, where the value that is to be assigned depends on the value of the expression in the switch statement. For example, this is true for the switch statement in the previous subsection.

This type of thing can be handled more elegantly by using a switch expression instead of a switch statement. Like any expression, a switch expression computes and returns a single value. The syntax is similar to a switch statement, but instead of a statement in each case, there is an expression (or a throw statement). For example,

String computerMove = switch ( (int)(3*Math.random()) ) {
        case 1 -> "Rock";
        case 2 -> "Paper";
        default -> "Scissors";
    };

This is a single assignment statement, where the value to be assigned to computerMove is computed by the switch expression. The semicolon on the last line is the semicolon that is required at the end of the assignment statement; it is not part of the switch expression.

A switch expression must always compute a value (or throw an exception) and therefore will usually have a default case. The expression in a case can be replaced by a block containing several statements; the value for that case should then be specified by a yield statement, which takes a form such as "yield 42;". The value after the word yield is then returned as the value of the switch expression.

Of course, switch expressions are not limited to assignment statements. You can use a switch expression any place where any other kind of expression could be used, such as in an output statement or as part of a larger expression.


3.6.6  The Traditional switch Statement

The older traditional syntax for switch statements is more complicated, but you should be aware of it since it has been widely used in Java and in other programming languages. As it is most often used, the traditional switch has the form:

switch (expression) {
   case constant-1:
      statements-1
      break;
   case constant-2:
      statements-2
      break;
      .
      .   // (more cases)
      .
   case constant-N:
      statements-N
      break;
   default:  // optional default case
      statements-(N+1)
} // end of switch statement

Note that in the traditional syntax, only one constant is allowed in a case label (but Java 17 allows a comma-separated list of constants here). A case label can be followed by any number of statements. This traditional syntax uses a colon after each case label, rather than ->. The default case is optional.

To execute this switch statement, the computer will evaluate the expression and jump to the case label that contains that constant, if there is one, or to the default case if not. The break statements in this switch are not actually required by the syntax of the switch statement. The effect of a break is to make the computer jump past the end of the switch statement, skipping over all the remaining cases. If you leave out the break statement, the computer will just forge ahead after completing one case and will execute the statements associated with the next case label. This is called "fall through"; it is rarely what you want, and it is a common source of bugs. However, it is legal and is even occasionally useful.

Note that you can leave out one of the groups of statements entirely (including the break). You then have two case labels in a row, containing two different constants. This just means that the computer will jump to the same place and perform the same action for each of the two constants.

Here is how our first example switch statement would be written using the traditional syntax:

switch ( N ) {   // (Assume N is an integer variable.)
   case 1:
      System.out.println("The number is 1.");
      break;
   case 2:
   case 4:
   case 8:
      System.out.println("The number is 2, 4, or 8.");
      System.out.println("(That's a power of 2!)");
      break;
   case 3:
   case 6:
   case 9:
      System.out.println("The number is 3, 6, or 9.");
      System.out.println("(That's a multiple of 3!)");
      break;
   case 5:
      System.out.println("The number is 5.");
      break;
   default:
      System.out.println("The number is 7 or is outside the range 1 to 9.");
}

[ Previous Section | Next Section | Chapter Index | Main Index ]