JAVA / OPERATORS
Ternary conditionals for compact branching
Use Java's conditional operator to select a value inline, predict the type it produces, and recognise when a chain should go back to if/else.
What you will learn
- Replace a value-producing if/else with cond ? a : b in an initializer or argument
- Predict a ternary's type when branches mix int, double, or boxed wrappers
- Wrap a ternary in parentheses when it sits next to + in string concatenation
- Chain two or three ternaries as a cascade, and recognise when if/else is clearer
Understanding Ternary conditionals for compact branching
Java has exactly one operator that takes three operands: a boolean condition, then two result expressions separated by ? and :. It is an expression rather than a statement, which is the entire point, because it evaluates to a value and can therefore sit anywhere a value is expected: a variable initializer, a method argument, the thing after return. That is also why the second branch is mandatory; an expression has to hand back something no matter how the test turns out, so there is no one-armed ternary the way there is an if without an else.
At runtime the condition is evaluated first and then exactly one branch runs, while the other is never touched, so side effects hidden inside it simply do not happen. At compile time, though, the compiler inspects both branches and works out one single type for the whole expression, applying the same widening and boxing rules it uses everywhere else. That split is the mental model worth keeping: the value comes from one branch, the type comes from both. It is why true ? 1 : 2.0 has type double and prints 1.0 even though the int branch is the one that wins.
The conditional operator sits near the bottom of the precedence table, just above assignment, so comparisons and arithmetic on either side of it bind first and rarely need parentheses of their own. String concatenation with + also binds first, and that is the one place beginners get bitten: "n = " + n > 0 ? x : y tries to compare a String with 0 and refuses to compile. The operator is right-associative, so a ? x : b ? y : z groups as a ? x : (b ? y : z) and reads as a top-to-bottom cascade of tests, which stays clear for two or three cases and becomes a puzzle beyond that.
public class Ternary {
public static void main(String[] args) {
int stock = 3;
String label = stock == 0 ? "sold out"
: stock < 5 ? "low stock"
: "in stock";
System.out.println(label);
int a = 14, b = 9;
System.out.println("larger = " + (a > b ? a : b));
System.out.println("stock is " + (stock == 1 ? "1 unit" : stock + " units"));
System.out.println(true ? 1 : 2.0);
}
}A conditional expression takes its value from the single branch that runs, but takes its type from both branches at compile time.
Worked examples
Only the chosen branch is evaluated
Shows that the branch not selected never executes, so its side effects never occur.
public class LazyBranch {
static int calls = 0;
static int bump(int value) {
calls++;
return value;
}
public static void main(String[] args) {
int limit = 10;
int chosen = limit > 5 ? bump(100) : bump(200);
System.out.println("chosen = " + chosen);
System.out.println("calls = " + calls);
}
}Example explained
Line 1limit > 5 is evaluated first and decides which of the two calls happens at all.
Line 2bump(100) runs and bump(200) is skipped entirely, so the counter stops at 1 instead of 2.
Line 3Because an unselected operand is never evaluated, a ternary can guard a call that would be expensive or unsafe.
The result type comes from both branches
Demonstrates how mixed branch types are unified, including the unboxing that turns a null wrapper into a NullPointerException.
public class TernaryTypes {
public static void main(String[] args) {
int count = 7;
System.out.println(count > 0 ? "positive" : "not positive");
long ticks = 5L;
System.out.println(count > 0 ? ticks : count);
Integer maybe = null;
try {
int value = count > 0 ? maybe : 0;
System.out.println(value);
} catch (NullPointerException e) {
System.out.println("unboxed null");
}
}
}Example explained
Line 1Both branches of the first ternary are String, so the expression is a String and println prints it directly.
Line 2ticks is long and count is int, so the expression type is long; the int branch would be widened if it were the one chosen.
Line 3Mixing Integer with int makes the expression type int, which forces an implicit maybe.intValue() call.
Line 4That hidden unboxing throws NullPointerException even though no method call is visible in the source.
Important notes
The condition must be boolean or Boolean; Java will not treat an int or a reference as a truth value, so n ? a : b is a compile error.
Assigning the result to a wide type does not change the expression's own type: double d = flag ? 1 : 2; still computes an int and only widens afterwards.
Common mistakes
Writing System.out.println("n = " + n > 0 ? "yes" : "no"); which does not compile, because + binds tighter than ?: so Java tries to compare a String with 0 and reports bad operand types for binary operator '>'.
Using a ternary to pick between two actions, as in n > 0 ? System.out.println("a") : System.out.println("b"); which fails to compile because void cannot be an operand and a conditional expression is not a legal statement on its own.
Mixing a wrapper with a primitive, as in Integer id = null; int n = flag ? id : 0; which compiles cleanly and then throws NullPointerException at runtime because the expression type is int and id must be unboxed.
Try it yourself
Change, predict, then run
Declare int score = 84; and print A for 90 and above, B for 80 and above, C for 70 and above, otherwise F, using one chained ternary and no if statement. Then set score to 62 and confirm the output changes to F.
Open the Java workspaceCheck your understanding
What does System.out.println(true ? 10 : 2.5); print, and what explains it?
- 10, because the condition is true and the int branch is the one evaluated
- 10.0, because the expression's type is unified to double before the value is printed
- 2.5, because the last branch determines the type of the expression
- Nothing, because it does not compile: the two branches have different types
Show answer
int and double are unified by binary numeric promotion, so the conditional expression has type double and the selected 10 is widened to 10.0 before println ever sees it. Answering 10 is tempting because the branch that runs holds an int literal, but a branch's own type never survives on its own; the expression has one compile-time type derived from both branches, which also rules out the compile-error option.