A bit of theory about Java Switch
Imagine that you're a knight stopped at a fork in the road. If you go left, you will lose your horse. If you go right, you will gain knowledge. How would we represent this situation in code? You probably already know that we use constructs like if-then and if-then-else to make these decisions.
if (turn_left) {
System.out.println("You will lose your horse");
}
if (turn_right) {
System.out.println("You will gain knowledge");
}
else
System.out.println("So you're just going to stand there?");
But what if the road splits not into two, but into ten? You have roads that are "completely to the right", "slightly to the left of that", "a little bit more to the left" and so on, totaling 10 possible roads?
Imagine how your "if-then-else" code will grow in this version!
if (option1)
{…}
else if (option2)
{…}
…
else if (optionN) ...
Suppose you have a 10-way fork in the road (it's important here that the number of options is finite). For such situations, Java has the switch statement.
switch (ExpressionForMakingAChoice) {
case (Value1):
Code1;
break;
case (Value2):
Code2;
break;
...
case (ValueN):
CodeN;
break;
default:
CodeForDefaultChoice;
break;
}
This is how the statement works:
- ExpressionForMakingAChoice is evaluated. Then the switch statement compares the resulting value with the next ValueX (in the order in which they are listed).
- If ExpressionForMakingAChoice matches ValueX, then the code following the colon is executed.
- If a break statement is encountered, then control is transferred outside the switch statement.
- If ExpressionForMakingAChoice doesn't match any ValueX, then control passes to CodeForDefaultCase.
In the switch statement, the type of ExpressionForMakingAChoice must be one of the following:
- byte, short, char, int.
- Byte, Short, Character, Integer (wrappers of the primitive datatypes).
- String.
- Enum.
- The default block is optional. If it is absent and ExpressionForMakingAChoice doesn't match any ValueX, then no action will be executed.
- The break statement is not required. If it is absent, the code will continue to be executed (ignoring further comparisons in the case statements) until the first occurrence of break or until the end of the switch statement.
- If the same code needs to be executed for several choices, we can eliminate duplication by specifying several consecutive case statements.
Let's now take a look at how the switch statement is used in Java
Don't you worry: we're done with the theory. After you see the following examples, everything will become much clearer. Well, let's get started. Let's look at an example from astronomy involving the planets of our solar system. In accordance with the latest international attitudes, we've excluded Pluto (due to the properties of its orbit). We recall that our planets are arranged by their distance from the Sun as follows: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune. Let's write a Java method that takes a planet's ordinal number (relative to its distance from the Sun) and returns the main components of the planet's atmosphere as a List<String>. You'll recall that some planets have a similar atmospheric composition. Thus, Venus and Mars contain mainly carbon dioxide; the atmosphere of Jupiter and Saturn consists of hydrogen and helium; and Uranus and Neptune add methane to the last pair of gases. Here's our function:
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("No atmosphere");
break;
case 2:
case 4: result.add("Carbon dioxide");
break;
case 3: result.add("Carbon dioxide");
result.add("Nitrogen");
result.add ("Oxygen");
break;
case 5:
case 6: result.add("Hydrogen");
result.add("Helium");
break;
case 7:
case 8: result.add("Methane");
result.add("Hydrogen");
result.add("Helium");
break;
default:
break;
}
return result;
}
Note that we're using the same code for planets with identical atmospheric compositions. We did this by using consecutive case statements.
If we want to get the composition of the atmosphere of our home planet, we call our method with 3 as the argument:
getPlanetAtmosphere(3).
System.out.println(getPlanetAtmosphere(3)) returns ["Carbon dioxide", "Nitrogen", "Oxygen"].
Experiment with break:
What happens if we remove all the break statements? Let's give it a try:
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("No atmosphere");
case 2:
case 4: result.add("Carbon dioxide");
case 3: result.add("Carbon dioxide");
result.add("Nitrogen");
result.add ("Oxygen");
case 5:
case 6: result.add("Hydrogen");
result.add("Helium");
case 7:
case 8: result.add("Methane");
result.add("Hydrogen");
result.add("Helium");
default:
}
return result;
}
If we print the result of System.out.println(getPlanetAtmosphere(3)), then we find that our home planet isn't so liveable. Or is it? Judge for yourself:
["Carbon dioxide", "Nitrogen", "Oxygen", "Hydrogen", "Helium", "Methane", "Hydrogen", "Helium"].
Why did this happen? The program executes all the case statements after the first match until the end of the switch block.
Excessive optimization of break statements
Note that we can improve the method by arranging the break statements and cases differently.
public static List<String> getPlanetAtmosphere(int seqNumberFromSun) {
List<String> result = new ArrayList<>();
switch (seqNumberFromSun) {
case 1: result.add("No atmosphere");
break;
case 3: result.add("Nitrogen");
result.add ("Oxygen");
case 2:
case 4: result.add("Carbon dioxide");
break;
case 7:
case 8: result.add("Methane");
case 5:
case 6: result.add("Hydrogen");
result.add("Helium");
}
return result;
}
Looks like less code, right? We've reduced the total number of statements by playing around with the order of the case statements and regrouping them. Now each type of gas is added to the list in only one line of code.
The code given in the last example is only to show how things work. We do not recommended writing code this way. If the author of such Java code (let alone other programmers) must maintain it, he or she will find it very difficult to reconstruct the logic behind the formation of those case blocks and the code executed in the switch statement.
Differences from if
Given the outward similarities of if and switch statements, don't forget that the switch statement selects one of the cases based on a SPECIFIC VALUE, whereas the if statement can have any boolean expression. Keep this in mind when designing your code.Case Label Restrictions: What You Can and Can’t Use
First things first, let's talk about case labels. You might be thinking, "Can I use any expression here?" Well, not exactly. Java has some strict rules about what you can put after the case
keyword.
Here’s the golden rule: Case labels must be constant expressions. This means they need to be values that the compiler can determine at compile-time. No variables allowed!
Invalid Case Label Example:
int number = 2;
switch (number) {
case number: // Error! 'number' is a variable, not a constant.
System.out.println("Invalid case label.");
break;
}
Valid Case Label Example:
final int CONST_NUMBER = 2; // 'final' makes this a constant.
switch (CONST_NUMBER) {
case 2:
System.out.println("This is valid!");
break;
}
Key Takeaways:
- Case labels must be constant expressions (like
final
variables, literals, orenum
constants). - You cannot use variables or expressions that change at runtime.
Visualizing the Switch-Case with a Flowchart
Sometimes, a picture really is worth a thousand words. Let’s take a look at how the switch-case
statement works using a flowchart. This will help you see how control flows through the different cases.
Breaking Down the Flowchart
Okay, now let's make sense of that flowchart. Here’s how it works step by step:
- Start: The program begins executing.
- Evaluate Expression: The value in the
switch
statement is evaluated. - Compare Cases: The program checks each
case
label in order. - Match Found: If a match is found, the corresponding code block executes.
- Break? If there’s a
break
statement, the program exits theswitch
. If not, it keeps executing the next case (this is called fall-through). - Default: If no match is found, the
default
block (if it exists) runs. - End: The program continues after the
switch
block.
See? Not so scary when you break it down like this!
Why Use Flowcharts to Understand Switch-Case Logic?
Now you might be wondering, "Why bother with flowcharts?" Great question!
- Visual Clarity: Flowcharts give you a big-picture view of how decisions are made in your program.
- Easier Debugging: They help identify where logic might be going wrong, especially with complex conditions.
- Faster Learning: For visual learners, seeing the process can speed up understanding.
- Better Documentation: Flowcharts make it easier to explain code to teammates or future you!
Example of a Switch-Case in Action
Let’s bring it all together with a practical example!
Example: Game Level Selector
import java.util.Scanner;
public class GameLevel {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Select your level: 1 (Easy), 2 (Medium), 3 (Hard)");
int level = scanner.nextInt();
switch (level) {
case 1:
System.out.println("You've chosen Easy mode. Good luck!");
break;
case 2:
System.out.println("You've chosen Medium mode. Let's go!");
break;
case 3:
System.out.println("You've chosen Hard mode. Brace yourself!");
break;
default:
System.out.println("Invalid choice. Please select 1, 2, or 3.");
}
}
}
In this example, the program checks the user’s input and executes the corresponding block. The break
ensures that only one block runs, preventing the dreaded fall-through!
Conclusion
- Use the case statement for more than two branches in order to not clutter your code with if statements.
- Don't forget to complete the logical block of the branch for each specific value (case statement) by inserting a break statement.
- The switch statement's expression can be an Enum or String, as well as some primitive types.
- Remember the default block. Use it to handle unexpected values.
- To optimize performance, move the code branches corresponding to the most common values to the beginning of the switch block.
- Don't get carried away in your "optimization" by deleting the break statements at the end of the case statements – such code is difficult to understand, and, as a result, difficult to maintain.
GO TO FULL VERSION