Chapter 6
IN THIS CHAPTER
Avoiding the trouble with big else-if statements
Using the switch statement
Creating case groups
Using characters with case
In Book 2, 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 making one particular type of decision: 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. You want to keep the switch
statement handy for use when such a need arises.
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 2, 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, with hundreds of lines of code that string else-if
after else-if
. The switch
statement provides a much more concise alternative.
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 a 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.\n\n"
+ "If your voting machine generates "
+ "an error code,\n"
+ "you can use this program to determine "
+ "the exact\ncause of the error.\n");
System.out.print("Enter the error code: ");
int err = sc.nextInt();
String msg;
if (err==1)
msg = "Voter marked more than one candidate.\n"
+ "Ballot rejected.";
else if (err==2)
msg = "Box checked and write-in candidate "
+ "entered.\nBallot rejected.";
else if (err==3)
msg = "Entire ballot was blank.\n"
+ "Ballot filled in according to "
+ "secret plan.";
else if (err==4)
msg = "Nothing unusual about the ballot.\n"
+ "Voter randomly selected for tax audit.";
else if (err==5)
msg = "Voter filled in every box.\n"
+ "Ballot counted twice.";
else if (err==6)
msg = "Voter drooled in voting machine.\n"
+ "Beginning spin cycle.";
else if (err==7)
msg = "Voter lied to pollster after voting.\n"
+ "Voter's ballot changed "
+ "to match polling data.";
else
msg = "Voter filled out ballot correctly.\n"
+ "Ballot discarded anyway.";
System.out.println(msg);
}
}
Wow! And this program has to decipher just 7 error codes. What if the machine had 500 codes?
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 useful when you need to select one of several alternatives based on the value of an int
, char
, String
, or enum
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 all the messages are 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.\n\n"
+ "If your voting machine generates "
+ "an error code,\n"
+ "you can use this program to determine "
+ "the exact\ncause of the error.\n");
System.out.print("Enter the error code: ");
int err = sc.nextInt();
String msg;
switch (err)
{
case 1:
msg = "Voter marked more than one "
+ "candidate.\nBallot rejected.";
break;
case 2:
msg = "Box checked and write-in candidate "
+ "entered.\nBallot rejected.";
break;
case 3:
msg = "Entire ballot was blank.\n"
+ "Ballot filled in according to "
+ "secret plan.";
break;
case 4:
msg = "Nothing unusual about the ballot.\n"
+ "Voter randomly selected for tax audit.";
break;
case 5:
msg = "Voter filled in every box.\n"
+ "Ballot counted twice.";
break;
case 6:
msg = "Voter drooled in voting machine.\n"
+ "Beginning spin cycle.";
break;
case 7:
msg = "Voter lied to pollster after voting.\n"
+ "Voter's ballot changed "
+ "to match polling data.";
break;
default:
msg = "Voter filled out ballot correctly.\n"
+ "Ballot discarded anyway.";
break;
}
System.out.println(msg);
}
}
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
, char
, String
, or enum
. It can’t be a long
or a floating-point type.
You can code as many case groups as you want or need. Each group begins with the word case
, followed by a constant (usually, a simple numeric or String 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 an optional 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.
Okay, the voting machine error decoder was kind of fun. Here’s a more down-to-earth example. Suppose that you need to set a commission rate based on a sales class represented by an integer (1, 2, or 3) according to this table:
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 in Figure 4-3 (Book 2, Chapter 4), because the operation of the switch
statement is similar to the operation of a series of else-if
statements.
You’re free to include any type of statements you want in the case groups, including if
statements. Suppose that your commission structure depends on total sales as well as sales class, as in this table:
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% |
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 here to see, folks. Move along.
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 lowercase and uppercase letters. Suppose that you need to set the commission rates for the sales class based on character codes rather than on integer values, according to this table:
Class |
Commission Rate |
A or a |
2% |
B or b |
3.5% |
C or c |
5% |
Any other value |
0% |
Here’s a switch
statement that can 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 control falls through to the case 'a'
group.
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. Many applications have features that are progressively added based on a control variable. Your local car wash, for example, may sell several packages with different services, as in this table:
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 is matched, 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!\n\n");
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 += "\tNew Car Scent, plus … \n";
case 'D':
case 'd':
details += "\tTire Treatment, plus … \n";
case 'C':
case 'c':
details +=
"\tLeather/Vinyl Treatment, plus … \n";
case 'B':
case 'b':
details += "\tWax, plus … \n";
case 'A':
case 'a':
details += "\tWash, vacuum, and hand dry.\n";
break;
default:
details = "That's not one of the codes.";
break;
}
System.out.println("\nThat package includes:\n");
System.out.println(details);
}
}
LISTING 6-4 A Version of the Car Wash Program That Prevents 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!\n\n");
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 one of the codes.";
break;
}
System.out.println("\nThat package includes:\n");
System.out.println(details);
}
public static String packageA()
{
return "\tWash, vacuum, and hand dry.\n";
}
public static String packageB()
{
return "\tWax, plus … \n";
}
public static String packageC()
{
return "\tLeather/Vinyl Treatment, plus … \n";
}
public static String packageD()
{
return "\tTire Treatment, plus … \n";
}
public static String packageE()
{
return "\tNew Car Scent, plus … \n";
}
}
Listing 6-5 shows a version of the car wash program that uses the string codes PRESIDENTIAL
, ELITE
, DELUXE
, SUPER
, and STANDARD
as the car wash types, instead of the letters A through E. Notice that to allow for variations in how a user might capitalize these codes, the user’s input is converted to all capital letters before it is tested against the string constants in the switch
statement.
LISTING 6-5 A Version of the Car Wash Program That Uses a String
import java.util.Scanner;
public class CarWashStringApp
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.println("The car wash application\n\n");
System.out.print("Enter the package code: ");
String s = sc.next();
String details = "";
switch (s.toUpperCase())
{
case "PRESIDENTIAL":
details += "\tNew Car Scent, plus … \n";
case "ELITE":
details += "\tTire Treatment, plus … \n";
case "DELUXE":
details += "\tLeather/Vinyl Treatment, plus … \n";
case "SUPER":
details += "\tWax, plus … \n";
case "STANDARD":
details += "\tWash, vacuum, and hand dry.\n";
break;
default:
details = "That's not one of the codes.";
break;
}
System.out.println("\nThat package includes:\n");
System.out.println(details);
}
}
With Java 13, several new features were added to the switch
statement.
The first improvement to the switch
statement is that you can now now list more than one value in the case
clause. The values must be separated by commas, as in this example:
case 'B', 'b':
In this example, either the value B
or b
will match the case. Prior to Java 13, it was necessary to code two case
clauses in a row to accomplish this.
The other major new addition to the switch
statement is that the switch
statement itself can now return a value. You can, therefore, use a switch
statement on the right side of an assignment statement, like this:
String msg = switch (p)
In this example, the value returned by the switch
statement is assigned to the variable msg
.
There are two ways you can provide a return value for a switch
statement. The first is to use the new yield
statement, which has a function similar to the break
statement but provides a return value. Here's an example:
String msg = switch (p)
{
case 'A', 'a'
yield "Wash, vacuum, and hand dry.";
};
Here, the value “Wash, vacuum, and hand dry.
” is provided as the return value for the switch
statement.
Note that when a switch
statement returns a value and is used in an assignment statement, you must add a semicolon to the end of the statement. Strictly speaking, it isn't the switch
statement that requires the semicolon; it’s the assignment statement that requires the semicolon.
The second way to return a value in a switch
statement is to use an arrow operator directly in the case
clause, like this:
case 'A', 'a' -> "Wash, vacuum, and hand dry.";
The arrow operator is more concise than the yield
statement, but there are few additional considerations for its use:
case
clause.case
clause itself becomes a statement, which must be terminated with a semicolon.case
clause, you must enclose the statements in a block, and the block must include a yield
statement to provide the yielded value. For example:
case 'A', 'a' -> {
System.out.println("Package A”);
yield "Wash, vacuum, and hand dry.";
}
default
clause to provide a default return value.Note that you can't mix and match the two methods for returning a value within a single switch
statement. In other words, if you use the arrow operator for case
clause, you must use it for all the case
clauses.
Listing 6-6 shows a version of the Car Wash program that uses these new switch
features.
LISTING 6-6 A Version of the Car Wash Program That Uses Java 13 Switch Statement Features
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!\n\n");
System.out.print("Enter the package code: ");
String s = sc.next();
char p = s.charAt(0);
String details = switch (p)
{
case 'E','e' -> packageE() + packageD() + packageC()
+ packageB() + packageA();
case 'D','d' -> packageD() + packageC()
+ packageB() + packageA();
case 'C','c' -> packageC() + packageB() + packageA();
case 'B','b' -> packageB() + packageA();
case 'A','a' -> packageA();
default -> "That's not one of the codes.";
};
System.out.println("\nThat package includes:\n");
System.out.println(details);
}
public static String packageA()
{
return "\tWash, vacuum, and hand dry.\n";
}
public static String packageB()
{
return "\tWax, plus … \n";
}
public static String packageC()
{
return "\tLeather/Vinyl Treatment, plus … \n";
}
public static String packageD()
{
return "\tTire Treatment, plus … \n";
}
public static String packageE()
{
return "\tNew Car Scent, plus … \n";
}
}