Figure 11-1: Running the code of Listing 11-1.
Chapter 11
How to Flick a Virtual Switch
In This Chapter
Dealing with many alternatives
Jumping out from the middle of a statement
Handling alternative assignments
Imagine playing Let’s Make a Deal with ten different doors. “Choose door number 1, door number 2, door number 3, door number 4. . . . Wait! Let’s break for a commercial. When we come back, I’ll say the names of the other six doors.”
What Monty Hall needs is Java’s switch
statement.
Meet the Switch Statement
The code in Listing 9-2 in Chapter 9 simulates a fortune-telling toy — an electronic oracle. Ask the program a question, and the program randomly generates a yes or no answer. But, as toys go, the code in Listing 9-2 isn’t much fun. The code has only two possible answers. There’s no variety. Even the earliest talking dolls could say about ten different sentences.
Suppose that you want to enhance the code of Listing 9-2. The call to my Random.nextInt(10) + 1
generates numbers from 1 to 10. So maybe you can display a different sentence for each of the ten numbers. A big pile of if
statements should do the trick:
if (randomNumber == 1) {
System.out.println(“Yes. Isn’t it obvious?”);
}
if (randomNumber == 2) {
System.out.println(“No, and don’t ask again.”);
}
if (randomNumber == 3) {
System.out.print(“Yessir, yessir!”);
System.out.println(“ Three bags full.”);
}
if (randomNumber == 4)
.
.
.
if (randomNumber < 1 || randomNumber > 10) {
System.out.print(“Sorry, the electronic oracle”);
System.out.println(“ is closed for repairs.”);
}
But that approach seems wasteful. Why not create a statement that checks the value of randomNumber
just once and then takes an action based on the value that it finds? Fortunately, just such a statement exists: the switch statement. Listing 11-1 has an example of a switch
statement.
Listing 11-1: An Answer for Every Occasion
import java.util.Scanner;
import java.util.Random;
import static java.lang.System.out;
class TheOldSwitcheroo {
public static void main(String args[]) {
Scanner myScanner = new Scanner(System.in);
Random myRandom = new Random();
int randomNumber;
out.print(“Type your question, my child: “);
myScanner.nextLine();
randomNumber = myRandom.nextInt(10) + 1;
switch (randomNumber) {
case 1:
out.println(“Yes. Isn’t it obvious?”);
break;
case 2:
out.println(“No, and don’t ask again.”);
break;
case 3:
out.print(“Yessir, yessir!”);
out.println(“ Three bags full.”); break;
case 4:
out.print(“What part of ‘no’”);
out.println(“ don’t you understand?”);
break;
case 5:
out.println(“No chance, Lance.”);
break;
case 6:
out.println(“Sure, whatever.”);
break;
case 7:
out.print(“Yes, but only if”);
out.println(“ you’re nice to me.”);
break;
case 8:
out.println(“Yes (as if I care).”);
break;
case 9:
out.print(“No, not until”);
out.println(“ Cromwell seizes Dover.”);
break;
case 10:
out.print(“No, not until”);
out.println(“ Nell squeezes Rover.”);
break;
default:
out.print(“You think you have”);
out.print(“ problems?”);
out.print(“ My random number”);
out.println(“ generator is broken!”);
break;
}
out.println(“Goodbye”);
}
}
The cases in a switch statement
Figure 11-1 shows three runs of the program in Listing 11-1. Here’s what happens during one of these runs:
The user types a heavy question, and the variable
randomNumber
gets a value. In the second run of Figure 11-1, this value is 2.
Execution of the code in Listing 11-1 reaches the top of the
switch
statement, so the computer starts checking this statement’s case
clauses. The value 2 doesn’t match the topmost case
clause (the case 1
clause), so the computer moves on to the next case
clause.
The value in the next
case
clause (the number 2) matches the value of the randomNumber
variable, so the computer executes the statements in this case 2
clause. These two statements are
out.println(“No, and don’t ask again.”);
break;
The first of the two statements displays No, and don’t ask again
on the screen. The second statement is called a break statement. (What a surprise!) When the computer encounters a break
statement, the computer jumps out of whatever switch
statement it’s in. So in Listing 11-1, the computer skips right past case 3
, case 4
, and so on. The computer jumps to the statement just after the end of the switch
statement.
The computer displays
Goodbye
because that’s what the statement after the switch
statement tells the computer to do.
Figure 11-1: Running the code of Listing 11-1.
The overall idea behind the program in Listing 11-1 is illustrated in Figure 11-2.
Figure 11-2: A fork with 11 prongs.
The default in a switch statement
What if something goes terribly wrong during a run of the Listing 11-1 program? Suppose that the expression myRandom.nextInt(10) + 1
generates a number that’s not in the 1 to 10 range. Then the computer responds by dropping past all the case
clauses. Instead of landing on a case
clause, the computer jumps to the default
clause. In the default
clause, the computer displays You think you have problems?...
, and then breaks out of the switch
statement. After the computer is out of the switch
statement, the computer displays Goodbye
.
Picky details about the switch statement
A switch
statement can take the following form:
switch (
Expression
) {
case
FirstValue
:
Statements
case
SecondValue
:
MoreStatements
// ... more cases...
default:
EvenMoreStatements
}
Here are some tidbits about switch
statements:
The
Expression
doesn’t have to have an int
value. It can be char
, byte
, short
, or int
.
For example, the following code works in Java 5 or later:
char letterGrade;
letterGrade =
myScanner.findWithinHorizon(“.”,0).charAt(0);
switch (letterGrade) {
case ‘A’:
System.out.println(“Excellent”);
break;
case ‘B’:
System.out.println(“Good”);
break;
case ‘C’:
System.out.println(“Average”);
break;
}
In fact, if you avoid using the Scanner
class and its findWithin Horizon
method, then this Bullet1’s switch
statement works with all versions of Java — old and new.
If you use Java 7 or later, you the
Expression
can be a String
. For example, the following code doesn’t work with Java 6, but works well in Java 7:
String description;
description = myScanner.next();
switch (description) {
case “Excellent”:
System.out.println(‘A’);
break;
case “Good”:
System.out.println(‘B’);
break;
case “Average”:
System.out.println(‘C’);
break;
}
The
Expression
doesn’t have to be a single variable. It can be any expression of type char
, byte
, short
, or int
. For example, you can simulate the rolling of two dice with the following code:
int die1, die2;
die1 = myRandom.nextInt(6) + 1;
die2 = myRandom.nextInt(6) + 1;
switch (die1 + die2) {
//...etc.
The cases in a
switch
statement don’t have to be in order. Here’s some acceptable code:
switch (randomNumber) {
case 2:
System.out.println(“No, and don’t ask again.”);
break;
case 1:
System.out.println(“Yes. Isn’t it obvious?”);
break;
case 3:
System.out.print(“Yessir, yessir!”);
System.out.println(“ Three bags full.”);
break;
//...etc.
This mixing of cases may slow you down when you’re trying to read a program, but it’s legal nonetheless.
You don’t need a
case
for each expected value of the Expression
. You can leave some expected values to the default
. Here’s an example:
switch (randomNumber) {
case 1:
System.out.println(“Yes. Isn’t it obvious?”);
break;
case 5:
System.out.println(“No chance, Lance.”);
break;
case 7:
System.out.print(“Yes, but only if”);
System.out.println(“ you’re nice to me.”);
break;
case 10:
System.out.print(“No, not until”);
System.out.println(“ Nell squeezes Rover.”);
break;
default:
System.out.print(“Sorry,”);
System.out.println(“ I just can’t decide.”);
break;
}
The
default
clause is optional.
switch (randomNumber) {
case 1:
System.out.println(“Yes. Isn’t it obvious?”);
break;
case 2:
System.out.println(“No, and don’t ask again.”);
break;
case 3:
System.out.print(“I’m too tired.”);
System.out.println(“ Go ask somebody else.”);
}
System.out.println(“Goodbye”);
If you have no default
clause, and a value that’s not covered by any of the cases comes up, then the switch
statement does nothing. For example, if randomNumber
is 4, then the preceding code displays Goodbye
, and nothing else.
In some ways,
if
statements are more versatile than switch
statements. For example, you can’t use a condition in a switch
statement’s Expression
:
//You can
’t do this:
switch (age >= 12 && age < 65)
You can’t use a condition as a case
value, either:
//You can
’t do this:
switch (age) {
case age <= 12: //...etc.
To break or not to break
At one time or another, every Java programmer forgets to use break
statements. At first, the resulting output is confusing, but then the programmer remembers fall-through. The term fall-through describes what happens when you end a case
without a break
statement. What happens is that execution of the code falls right through to the next case
in line. Execution keeps falling through until you eventually reach a break
statement or the end of the entire switch
statement.
If you don’t believe me, just look at Listing 11-2. This listing’s code has a switch
statement gone bad.
Listing 11-2: Please, Gimme a Break!
/*
* This isn’t good code. The programmer forgot some
* of the break statements.
*/
import java.util.Scanner;
import java.util.Random;
import static java.lang.System.out;
class BadBreaks {
public static void main(String args[]) {
Scanner myScanner = new Scanner(System.in);
Random myRandom = new Random();
int randomNumber;
out.print(“Type your question, my child: “);
myScanner.nextLine();
randomNumber = myRandom.nextInt(10) + 1;
switch (randomNumber) {
case 1:
out.println(“Yes. Isn’t it obvious?”);
case 2:
out.println(“No, and don’t ask again.”);
case 3:
out.print(“Yessir, yessir!”);
out.println(“ Three bags full.”);
case 4:
out.print(“What part of ‘no’”);
out.println(“ don’t you understand?”);
break
;
case 5:
out.println(“No chance, Lance.”);
case 6:
out.println(“Sure, whatever.”);
case 7:
out.print(“Yes, but only if”);
out.println(“ you’re nice to me.”);
case 8:
out.println(“Yes (as if I care).”);
case 9:
out.print(“No, not until”);
out.println(“ Cromwell seizes Dover.”);
case 10:
out.print(“No, not until”);
out.println(“ Nell squeezes Rover.”);
default:
out.print(“You think you have”);
out.print(“ problems?”);
out.print(“ My random number”);
out.println(“ generator is broken!”);
}
out.println(“Goodbye”);
}
}
I’ve put two runs of this code in Figure 11-3. In the first run, the random Number
is 7. The program executes cases 7 through 10, and the default
. In the second run, the randomNumber
is 3. The program executes cases 3 and 4. Then, because case 4 has a break
statement, the program jumps out of the switch
and displays Goodbye
.
Figure 11-3: Please make up your mind.
Using Fall-Through to Your Advantage
Often, when you’re using a switch
statement, you don’t want fall-through, so you pepper break
statements throughout the switch
. But, sometimes, fall-through is just the thing you need.
Take the number of days in a month. Is there a simple rule for this? Months containing the letter “r” have 31 days? Months in which “i” comes before “e” except after “c” have 30 days?
You can fiddle with if
conditions all you want. But to handle all the possibilities, I prefer a switch
statement. Listing 11-3 demonstrates the idea.
Listing 11-3: Finding the Number of Days in a Month
import java.util.Scanner;
class DaysInEachMonth {
public static void main(String args[]) {
Scanner myScanner = new Scanner(System.in);
int month, numberOfDays = 0;
boolean isLeapYear;
System.out.print(“Which month? “);
month = myScanner.nextInt();
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
numberOfDays = 31;
break;
case 4:
case 6:
case 9:
case 11:
numberOfDays = 30;
break;
case 2:
System.out.print(“Leap year (true/false)? “);
isLeapYear = myScanner.nextBoolean();
if (isLeapYear) {
numberOfDays = 29;
} else {
numberOfDays = 28;
}
}
System.out.print(numberOfDays);
System.out.println(“ days”);
}
}
Figure 11-4 shows several runs of the program in Listing 11-3. For month number 6, the computer jumps to case 6
. There are no statements inside the case 6
clause, so that part of the program’s run is pretty boring.
Figure 11-4: How many days until the next big deadline?
But with no break
in the case 6
clause, the computer marches right along to case 9
. Once again, the computer finds no statements and no break
, so the computer ventures to the next case, which is case 11
. At that point, the computer hits pay dirt. The computer assigns 30 to numberOfDays
, and breaks out of the entire switch
statement (see Figure 11-5).
Figure 11-5: Follow the bouncing ball.
February is the best month of all. For one thing, the February case in Listing 11-3 contains a call to the Scanner
class’s nextBoolean
method. The method expects me to type either true
or false
. The code uses whatever word I type to assign a value to a boolean
variable. (In Listing 11-3, I assign true
or false
to the isLeapYear
variable.)
February also contains its own if
statement. In Chapter 10, I nest if
statements within other if
statements. But in February, I nest an if
statement within a switch
statement. That’s cool.
Using a Conditional Operator
Java has a neat feature that I can’t resist writing about. Using this feature, you can think about alternatives in a very natural way.
And what do I mean by “a natural way?” If I think out loud as I imitate the if
statement near the end of Listing 11-3, I come up with this:
//The thinking in Listing 11-3:
What should I do next?
If this is a leap year,
I’ll make the
numberOfDays be 29;
Otherwise,
I’ll make the
numberOfDays be 28.
I’m wandering into an if
statement without a clue about what I’m doing next. That seems silly. It’s February, and everybody knows what you do in February. You ask how many days the month has.
In my opinion, the code in Listing 11-3 doesn’t reflect the most natural way to think about February. So here’s a more natural way:
//A more natural way to think about the problem:
The value of
numberOfDays is...
Wait! Is this a leap year?
If yes, 29
If no, 28
In this second, more natural way of thinking, I know from the start that I’m picking a number of days. So by the time I reach a fork in the road (Is this a leap year?
), the only remaining task is to choose between 29 and 28.
I can make the choice with finesse:
case 2:
System.out.print(“Leap year (true/false)? “);
isLeapYear = myScanner.nextBoolean();
numberOfDays =
isLeapYear ? 29 : 28
;
The ? :
combination is called a conditional operator. In Figure 11-6, I show you how my natural thinking about February can morph into the conditional operator’s format.
Figure 11-6: From your mind to the computer’s code.
Taken as a whole, isLeapYear ? 29 : 28
is an expression with a value. And what value does this expression have? Well, the value of isLeapYear ? 29 : 28
is either 29 or 28. It depends on whether isLeapYear
is or isn’t true. That’s how the conditional operator works:
If the stuff before the question mark is
true
, then the whole expression’s value is whatever comes between the question mark and the colon.
If the stuff before the question mark is
false
, then the whole expression’s value is whatever comes after the colon.
Figure 11-7 gives you goofy way to visualize these ideas.
Figure 11-7: Have you ever seen an expression talking to itself?
So the conditional operator’s overall effect is as if the computer is executing
numberOfDays = 29;
or
numberOfDays = 28;
One way or another, numberOfDays
gets a value, and the code solves the problem with style.