Pulling a Switcheroo

In Book II, Chapter 4, you find out about the workhorses of Java decision-making: boolean expressions and the mighty if statement. In this chapter, you discover another Java tool for decision making: the switch statement. The switch statement is a pretty limited beast, but it excels at one particular type of decision making: choosing one of several actions based on a value stored in an integer variable. As it turns out, the need to do just that comes up a lot. So you want to keep the switch statement handy when such a need arises.

else if Monstrosities

Many applications call for a simple logical selection of things to be done depending on some value that controls everything. As I describe in Book II, Chapter 4, such things are usually handled with big chains of else-if statements all strung together.

Unfortunately, these things can quickly get out of hand. else-if chains can end up looking like DNA double-helix structures, or those things that dribble down from the tops of the computer screens in The Matrix.

For example, Listing 6-1 shows a bit of a program that might be used to decode error codes in a Florida or Ohio voting machine.

Listing 6-1: The else-if Version of the Voting Machine Error Decoder

import java.util.Scanner; public class VoterApp { static Scanner sc = new Scanner(System.in); public static void main(String[] args) { System.out.println ("Welcome to the voting machine " + "error code decoder. " + "If your voting machine generates " + "an error code, " + "you can use this program to determine " + "the exact cause of the error. "); System.out.print("Enter the error code: "); int err = sc.nextInt(); String msg; if (err==1) msg = "Voter marked more than one candidate. " + "Ballot rejected."; else if (err==2) msg = "Box checked and write-in candidate " + "entered. Ballot rejected."; else if (err==3) msg = "Entire ballot was blank. " + "Ballot filled in according to " + "secret plan."; else if (err==4) msg = "Nothing unusual about the ballot. " + "Voter randomly selected for tax audit."; else if (err==5) msg = "Voter filled in every box. " + "Ballot counted twice."; else if (err==6) msg = "Voter drooled in voting machine. " + "Beginning spin cycle."; else if (err==7) msg = "Voter lied to pollster after voting. " + "Voter's ballot changed " + "to match polling data."; else msg = "Voter filled out ballot correctly. " + "Ballot discarded anyway."; System.out.println(msg); } }

Wow! And this program has to decipher just seven different error codes. What if the machine had 500 different codes?

A Better Version of the Voting Machine Error Decoder Program

Fortunately, Java has a special statement that's designed just for the kind of task represented by the Voting Machine Error Decoder program: the switch statement. Specifically, the switch statement is sometimes useful when you need to select one of several alternatives based on the value of an integer or character type variable.

Listing 6-2 shows a version of the Voting Machine Error Decoder program that uses a switch statement instead of a big else-if structure. I think you'll agree that this version of the program is a bit easier to follow. The switch statement makes it clear that the messages are all selected based on the value of the err variable.

Listing 6-2: The switch Version of the Voting Machine Error Decoder

import java.util.Scanner; public class VoterApp2 { static Scanner sc = new Scanner(System.in); public static void main(String[] args) { System.out.println ("Welcome to the voting machine " + "error code decoder. " + "If your voting machine generates " + "an error code, " + "you can use this program to determine " + "the exact cause of the error. "); System.out.print("Enter the error code: "); int err = sc.nextInt(); String msg; switch (err) { case 1: msg = "Voter marked more than one " + "candidate. Ballot rejected."; break; case 2: msg = "Box checked and write-in candidate " + "entered. Ballot rejected."; break; case 3: msg = "Entire ballot was blank. " + "Ballot filled in according to " + "secret plan."; break; case 4: msg = "Nothing unusual about the ballot. " + "Voter randomly selected for tax audit."; break; case 5: msg = "Voter filled in every box. " + "Ballot counted twice."; break; case 6: msg = "Voter drooled in voting machine. " + "Beginning spin cycle."; break; case 7: msg = "Voter lied to pollster after voting. " + "Voter's ballot changed" + "to match polling data."; break; default: msg = "Voter filled out ballot correctly. " + "Ballot discarded anyway."; break; } System.out.println(msg); } }

Using the switch Statement

The basic form of the switch statement is this:

switch (expression) { case constant: statements; break; [ case constant-2: statements; break; ] ... [ default: statements ; break; ] ... }

* The expression must evaluate to an int, short, byte, or char. It can't be a long or a floating-point type.

You can code as many case groups as you want or need. Each begins with the word case followed by a constant (usually a simple numeric literal) and a colon. Then you code one or more statements that you want executed if the value of the switch expression equals the constant. The last line of each case group is a break statement, which causes the entire switch statement to end.

The default group, which is optional, is like a catch-all case group. Its statements are executed only if none of the previous case constants match the switch expression.

  Tip 

Note that the case groups are not true blocks marked with braces. Instead, each case group begins with the case keyword and ends with the case keyword that starts the next case group. However, all the case groups together are defined as a block marked with a set of braces.

  Warning 

The last statement in each case group usually is a break statement. A break statement causes control to skip to the end of the switch statement. If you omit the break statement, control falls through to the next case group. Accidentally leaving out break statements is the most common cause of trouble with the switch statement.

A Boring Business Example Complete with Flowchart

Okay, the Voting Machine Error Decoder was kind of fun. Now here's a more down-to-earth example. Suppose you need to set a commission rate-based on a sales class represented by an integer that can be 1, 2, or 3-according to this table:

Open table as spreadsheet

Class

Commission Rate

1

2%

2

3.5%

3

5%

Any other value

0%

You could do this with the following switch statement:

double commissionRate; switch (salesClass) { case 1: commissionRate = 0.02; break; case 2: commissionRate = 0.035; break; case 3: commissionRate = 0.05; break; default: commissionRate = 0.0; break; }

Figure 6-1 shows a flowchart that describes the operation of this switch statement. As you can see, this flowchart is similar to the flowchart that was shown in Figure 4-3 in Book II, Chapter 4. That's because the operation of the switch statement is similar to the operation of a series of else-if statements.

Figure 6-1: The flowchart for a switch statement.

  Tip 

I like flowcharts because they remind me of my old days of computer programming, when we had to draw flowcharts for every program we wrote before we were allowed to write any code. The flowcharts didn't really help us write better programs, but they were fun to draw.

Putting if Statements Inside switch Statements

You're free to include any type of statements you want in the case groups, including if statements. For example, suppose your commission structure depends on total sales as well as sales class, like this:

Open table as spreadsheet

Class

Sales < $10,000

Sales $10,000 and Above

1

1%

2%

2

2.5%

3.5%

3

4%

5%

Any other value

0%

0%

Then you can use the following switch statement:

double commissionRate; switch (salesClass) { case 1: if (salesTotal < 10000.0) commissionRate = 0.01; else commissionRate = 0.02; break; case 2: if (salesTotal < 10000.0) commissionRate = 0.025; else commissionRate = 0.035; break; case 3: if (salesTotal < 10000.0) commissionRate = 0.04; else commissionRate = 0.05; break; default: commissionRate = 0.0; break; }

Here each case group includes an if statement. If necessary, these if statements could be complex nested if statements.

Other than the if statements within the case groups, there's nothing else here to see, folks. Move along.

Creating Character Cases

Aside from having a nice alliterative title, this section shows how you can use a char variable rather than an integer in a switch statement. When you use a char type, providing two consecutive case constants for each case group is common, to allow for both lower-and uppercase letters. For example, suppose you need to set the commission rates for the sales class-based on character codes rather than on integer values-according to this table:

Open table as spreadsheet

Class

Commission Rate

A or a

2%

B or b

3.5%

C or c

5%

Any other value

0%

Here's the switch statement to do the trick:

double commissionRate; switch (salesClass) { case 'A' : case 'a' : commissionRate = 0.02; break; case 'B' : case 'b' : commissionRate = 0.035; break; case 'C' : case 'c' : commissionRate = 0.05; break; default: commissionRate = 0.0; break; }

The key to understanding this example is realizing that you don't have to code any statements at all for a case group, and that if you omit the break statement from a case group, control falls through to the next case group. Thus the case ‘A’ group doesn't contain any statements, but it falls through to the case ‘a’ group.

  Warning 

You use apostrophes, not quotation marks, to create character literals.

Falling through the Cracks

Although the most common cause of problems with the switch statement is accidentally leaving out a break statement at the end of a case group, sometimes you need to do it on purpose. For example, many applications have features that are progressively added based on a control variable. For example, your local car wash might sell several packages with different services:

Open table as spreadsheet

Package

Services

A

Wash, vacuum, and hand dry

B

Package A + Wax

C

Package B + Leather/Vinyl Treatment

D

Package C + Tire Treatment

E

Package D + New Car Scent

Listing 6-3 shows an application that displays all the products you get when you order a specific package. It works by testing the package codes in a switch statement in reverse order (starting with package E) and adding the products that come with each package to the details variable. None of the case groups except the last includes a break statement. As a result, control falls through each case group to the next group. Thus, once a case group has tested true, the rest of the case groups in the switch statement are executed.

Listing 6-3: The Car Wash Application

import java.util.Scanner; public class CarWashApp { static Scanner sc = new Scanner(System.in); public static void main(String[] args) { System.out.println ("The car wash application! "); System.out.print("Enter the package code: "); String s = sc.next(); char p = s.charAt(0); String details = ""; switch (p) { case 'E': case 'e': details += " New Car Scent, plus... "; case 'D': case 'd': details += " Tire Treatment, plus... "; case 'C': case 'c': details += " Leather/Vinyl Treatment, plus... "; case 'B': case 'b': details += " Wax, plus... "; case 'A': case 'a': details += " Wash, vacuum, and hand dry. "; break; default: details = "That's break; } System.out.println(" That package includes: "); System.out.println(details); } }

  Tip 

Just between you and me, writing programs that depend on switch statements falling through the cracks (as in this example) isn't really a good idea. Instead, consider placing the statements for each case group in separate methods, and then calling all the methods you need for each case group. Then you can use a break statement at the end of each group to prevent falling through. Listing 6-4 shows a version of the car wash application that uses this technique to avoid fall-throughs in the switch statement. (Using simple fall-throughs to treat upper-and lowercase characters the same isn't as confusing, so this program still uses that technique.)

Listing 6-4: A Version of the Car Wash Program That Avoids Nasty Falls

import java.util.Scanner; public class CarWashApp2 { static Scanner sc = new Scanner(System.in); public static void main(String[] args) { System.out.println ("The car wash application! "); System.out.print("Enter the package code: "); String s = sc.next(); char p = s.charAt(0); String details = ""; switch (p) { case 'E': case 'e': details = packageE() + packageD() + packageC() + packageB() + packageA(); break; case 'D': case 'd': details = packageD() + packageC() + packageB() + packageA(); break; case 'C': case 'c': details = packageC() + packageB() + packageA(); break; case 'B': case 'b': details = packageB() + packageA(); break; case 'A': case 'a': details = packageA(); break; default: details = "That's not break; } System.out.println(" That package includes: "); System.out.println(details); } public static String packageA() { return " Wash, vacuum, and hand dry. "; } public static String packageB() { return " Wax, plus... "; } public static String packageC() { return " Leather/Vinyl Treatment, plus... "; } public static String packageD() { return " Tire Treatment, plus... "; } public static String packageE() { return " New Car Scent, plus... "; } }

Категории