Chapter 11

How to Flick a Virtual Switch

In This Chapter

arrow Dealing with many alternatives

arrow Jumping out from the middle of a statement

arrow 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:

check.png 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.

check.png 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.

check.png 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.

check.png 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.

9780470371749-fg1101.tif

The overall idea behind the program in Listing 11-1 is illustrated in Figure 11-2.

Figure 11-2: A fork with 11 prongs.

9780470371749-fg1102.eps

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.

tip.eps You don’t really need to put a break at the very end of a switch statement. In Listing 11-1, the last break (the break that’s part of the default clause) is just for the sake of overall tidiness.

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:

check.png 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.

check.png 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;

}

check.png 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.

check.png 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.

check.png 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;

}

check.png 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.

check.png 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 cant do this:

switch (age >= 12 && age < 65)

You can’t use a condition as a case value, either:

//You cant 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.

remember.eps The switch statement in Listing 11-2 is missing some break statements. Even without these break statements, the code compiles with no errors. But when you run the code in Listing 11-2, you don’t get the results that you want.

Figure 11-3: Please make up your mind.

9780470371749-fg1103.tif

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?

9780470371749-fg1104.tif

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.

9780470371749-fg1105.eps

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.

9780470371749-fg1106.eps

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:

check.png 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.

check.png 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?

9780470371749-fg1107.eps

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.