JAVA / BRANCHING AND SWITCH EXPRESSIONS
Choosing if, switch or ternary for a branch
Decide between if, switch and the ternary operator by asking whether a branch yields a value and whether its test is equality against constants.
What you will learn
- Decide first whether the branch yields a value or only performs an effect
- Use switch only when one value is compared against compile-time constants
- Reach for if when the test involves ranges, null checks or several variables
- Keep ternaries to two short results and never nest them for a third case
Understanding Choosing if, switch or ternary for a branch
Java gives you three ways to fork, and they are not interchangeable stylistic choices, because they are different kinds of construct. An if and a switch statement are statements: they run code and evaluate to nothing. The ternary ?: and the switch expression are expressions: they evaluate to a value, so they can sit on the right of an assignment, inside a string concatenation, or in an argument list. The first question is therefore what the branch is for, producing one value or performing an effect, because the answer already removes half the options.
The second question is the shape of the test. A case label is a compile-time constant that the selector is compared to for equality, so switch is only available when every path asks whether one value equals one of a known set. Ranges, null tests, comparisons between two variables, and anything joined by && or || have no label form, which is why they belong to if; this is a structural limit, not a matter of taste. When a branch does fit switch, prefer it: javac emits a single tableswitch or lookupswitch that dispatches on the value once instead of re-running a test per branch, and a reader can see immediately that only that one value is being examined.
The ternary is the narrowest tool: two outcomes, one value, short enough to read without stopping. It cannot contain statements, so flag ? save() : delete() is not a legal way to call two void methods, and its very low precedence means it usually needs parentheses once embedded in a larger expression. Its real payoff is initializing a final or effectively final variable in one shot, which if/else can only manage through a blank final plus one assignment per path. As soon as a third outcome appears, a switch expression over constants or a plain if/else reads better than a nested ternary.
public class BranchChoice {
// One value against known constants: switch, used as an expression.
static String reason(int status) {
return switch (status) {
case 200 -> "OK";
case 404 -> "Not Found";
case 500 -> "Internal Server Error";
default -> "Unassigned";
};
}
// Bands, not equality: no case label can hold ">= 50_000", so if wins.
static String tier(int cents) {
if (cents >= 50_000) {
return "gold";
} else if (cents >= 10_000) {
return "silver";
}
return "bronze";
}
// Two outcomes feeding one returned value: ternary.
static String display(String name) {
return (name == null || name.isBlank()) ? "(anonymous)" : name.trim();
}
public static void main(String[] args) {
System.out.println(reason(404));
System.out.println(reason(418));
System.out.println(tier(50_000));
System.out.println(tier(9_999));
System.out.println(display(null));
System.out.println(display(" Ada "));
}
}The form follows the branch: expressions when a single value is produced, if when the test is an arbitrary boolean, switch when the test is equality against known constants.
Worked examples
Where only an expression fits
Shows the one thing a ternary can do that an if cannot, and the price an if pays to match it.
public class ValueVsEffect {
public static void main(String[] args) {
int count = 1;
String configured = null;
System.out.println("Found " + count + (count == 1 ? " file" : " files"));
final int limit = configured != null ? Integer.parseInt(configured) : 10;
System.out.println("limit=" + limit);
final int limit2;
if (configured != null) {
limit2 = Integer.parseInt(configured);
} else {
limit2 = 10;
}
System.out.println("limit2=" + limit2);
}
}Example explained
Line 1The ternary can be an operand of + because it has a value; an if produces nothing and cannot appear inside the concatenation at all.
Line 2The parentheses are mandatory: without them the condition becomes ("Found " + count + count) == 1, which fails to compile since a String cannot be compared to an int with ==.
Line 3final int limit = ... ? ... : 10; initializes the variable in a single expression, which is the ternary's main practical advantage.
Line 4limit2 is a blank final, so the if/else must assign it exactly once on every path; the compiler checks that, which is why the statement form is still safe, just longer.
String dispatch: switch versus ==
Demonstrates that a String switch matches by value, unlike the reference comparison an if chain invites.
public class CommandDispatch {
static int apply(String cmd, int value) {
switch (cmd) {
case "inc": return value + 1;
case "dec": return value - 1;
case "zero": return 0;
default: throw new IllegalArgumentException("unknown: " + cmd);
}
}
public static void main(String[] args) {
String typed = new String("inc");
System.out.println(typed == "inc");
System.out.println(apply(typed, 41));
System.out.println(apply("zero", 41));
}
}Example explained
Line 1typed == "inc" is false because new String("inc") is a distinct object from the pooled literal, so reference comparison misses.
Line 2apply(typed, 41) still returns 42: javac compiles a String switch into a hashCode dispatch confirmed by equals, so the match is by value.
Line 3Every case returns immediately, so no break is involved and control never reaches a later label.
Line 4The trade-off: if cmd were null this switch throws NullPointerException, while an if chain written as "inc".equals(cmd) would not, so a nullable selector needs a guard first.
Important notes
A ternary whose operands mix Integer and int is a numeric conditional expression, so both sides are unboxed: flag ? boxedNull : 0 throws NullPointerException where the same choice written as if/else would quietly assign null.
Constant case labels accept only byte, short, char, int and their wrappers, String and enum constants, so a branch on a long, double or boolean value must use if or a ternary; arrow-label switch expressions also require Java 14 or newer.
Common mistakes
Trying to express a band or compound test as a case label, such as case cents >= 10_000 ->. It does not compile, because labels are constants matched by equality and never conditions, so the whole branch has to be rewritten with if.
Using a ternary for side effects, such as ok ? send(msg) : queue(msg); as a line of its own. A conditional expression is not a statement and void calls cannot be its operands, so the compiler rejects it and the code has to become if/else.
Dropping the parentheses in concatenation: System.out.println("n=" + n > 0 ? "high" : "low") groups as ("n=" + n) > 0 and fails with bad operand types for binary operator '>', because ?: binds far more loosely than +.
Try it yourself
Change, predict, then run
Write char grade(int score) using if with the bands 90, 80 and 70, and String remark(char grade) using a switch expression over 'A', 'B', 'C' and a default, then print remark(grade(83)). Add a one-line comment saying why the score version cannot be a switch.
Open the Java workspaceCheck your understanding
Which of these branches cannot be written as a switch over constant case labels and has to use if?
- Choosing a message from int httpStatus, where 200, 404 and 500 each have their own message
- Choosing a discount tier from int cents, where the bands are 50000 and above, 10000 and above, and everything else
- Choosing a colour name from an enum Suit that has four constants
- Choosing a handler from a String command whose values are "add", "del" and "list"
Show answer
Case labels are compile-time constants matched by equality, and a band such as 10000 and above has no constant label form, so the tier branch must be an if. Option 0 is tempting because it also switches on an int, but there each path tests equality against one specific constant, which is exactly the shape switch was built for.