Topic 2.2
The Conditional (Ternary) Operator
In one line
condition ? a : b is an expression that produces a when the condition is true and b otherwise. It's a compact if/else for choosing a value, with its own typing rules that can surprise you (numeric promotion and unboxing).
Think of it like this
A vending machine button labelled 'hot or cold?'. You press it, and one drink comes out, chosen by the weather. You don't get instructions back, you get a drink. The ternary operator is that button: you ask a yes/no question and get back one value.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Operand
- A value an operator works on. In
3 + 4, the operands are3and4. - Ternary
- Taking three operands.
?:is Java's only ternary operator. - Expression
- A piece of code that produces a value, like
2 + 3orage >= 18. - Statement
- A complete instruction that does something, like a variable declaration, an
if, or a method call ending in;. - Precedence
- The rule for which operator is applied first when several appear together, like
*before+in maths. - Numeric promotion
- Java converting a smaller number type to a bigger one (say
inttodouble) so two values share a type. - Unboxing
- Java turning a wrapper object such as
Integerinto its plain primitive (int) automatically. UnboxingnullthrowsNullPointerException.
Step by step
01From if/else to one line
Very often an if/else exists only to choose which value goes into a variable. The ternary says that directly, and lets you declare the variable and give it a value in one step.
Read age >= 18 ? "adult" : "minor" as 'is age at least 18? if so "adult", otherwise "minor"'.
int age = 20;
// With if/else: declare first, assign in two places
String label;
if (age >= 18) {
label = "adult";
} else {
label = "minor";
}
// With the ternary: one expression
String label2 = age >= 18 ? "adult" : "minor";
System.out.println(label + " " + label2); // adult adult02It's an expression, so use it where values go
Because it yields a value, a ternary can sit anywhere a value can: inside println, as a method argument, in a return, even in a string concatenation (in brackets).
It can't stand alone as a statement. x > 0 ? System.out.println("a") : System.out.println("b"); is rejected with error: not a statement, and println returns void anyway, which isn't a value. Use an if when you want to *do* something; use ?: when you want to *pick* something.
int items = 1;
System.out.println("You have " + items + (items == 1 ? " item" : " items"));
int a = 7, b = 12;
int bigger = a > b ? a : b; // same as Math.max(a, b)
System.out.println("Bigger: " + bigger);03Only one side runs
Java evaluates the condition first, then exactly one of the two value operands. The other one is never touched, so it can't throw and its side effects (like a method call that prints) don't happen.
This makes the ternary a safe guard: count == 0 ? 0 : total / count can't divide by zero.
04Precedence: why the brackets matter
The ?: operator binds more loosely than +, >= and ==. So in "Age: " + age >= 18 ? "adult" : "minor", Java first builds the string "Age: 20", then tries "Age: 20" >= 18, comparing a String with an int, which is an error.
Put brackets around the whole ternary: "Age: " + (age >= 18 ? "adult" : "minor").
int age = 20;
System.out.println("Age: " + age >= 18 ? "adult" : "minor"); // compile error
System.out.println("Age: " + (age >= 18 ? "adult" : "minor")); // Age: adult05The type of the result (where people get surprised)
A ternary has one compile-time type, chosen from both value operands (JLS 15.25). The everyday rules:
Both numbers → binary numeric promotion: true ? 1 : 2.0 has type double, so it prints 1.0, even though the int side was chosen.
**A char and an int constant that fits in a char** → the result is char: false ? 'a' : 98 prints b (98 is the code of b). With an int *variable* instead of the constant, the result is int and prints 98.
A wrapper and a primitive (Integer and int) → the wrapper is unboxed. If it's null, you get a NullPointerException from a line that doesn't seem to call anything.
Two reference types that aren't numbers (say String and Integer) → the result is a common supertype, like Object, and nothing is converted.
06The hidden NullPointerException
In int total = vip ? 100 : bonus; with Integer bonus = null, the result type is int (one side is int, the other Integer). So when vip is false, Java calls bonus.intValue() behind your back, and that call on null throws.
Since Java 14, helpful NullPointerException messages tell you which call failed. The variable shows as <local1> because javac doesn't store local variable names unless you compile with -g.
public class Main {
public static void main(String[] args) {
Integer bonus = null;
boolean vip = false;
int total = vip ? 100 : bonus; // unboxes bonus -> NPE
System.out.println(total);
}
}07Nesting: right-associative, and when to stop
a ? x : b ? y : z groups from the right: a ? x : (b ? y : z). It reads like an else-if ladder squeezed onto one line.
One nested level, laid out over several lines, can be fine. Beyond that, readers lose track of which : belongs to which ?. Switch to an if/else ladder, or to a switch expression (Topic 2.4), which is the modern way to pick a value from many cases.
int n = -4;
String sign = n > 0 ? "positive"
: n < 0 ? "negative"
: "zero";
System.out.println(sign); // negativeTry it yourself
- 1
Predict the types
Before running 'Typing surprises', cover the output and write down what each line prints. Then run it. Most people miss line 2 (
b) and line 4 (1.0 is a Double). - 2
Rewrite as if/else
Take the
signline from 'Choosing values with ?:' and rewrite it as an else-if ladder that assignssign. Which version do you find easier to read? Now add a fourth case, 'big' forn > 100, to both. - 3
See only one side run
In 'Only one side is evaluated', change
trueto1 > 2. Predict which 'evaluating' line appears, then run.
Code & diagrams
Expected output
Label: minor
Cart: 3 items
Max: 12
Average: 0
Sign: negativeExpected output
condition true:
evaluating left
result 1
condition false:
evaluating right
result 2Expected output
1.0
b
98
1.0 is a Double
1 is an IntegerBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget the brackets inside a concatenation
Write System.out.println("Age: " + age >= 18 ? "adult" : "minor");.
Break #2
Use a ternary as a statement
Write x > 0 ? System.out.println("a") : System.out.println("b"); on its own line.
Break #3
Mix Integer and int when the Integer is null
Write int total = vip ? 100 : bonus; with Integer bonus = null and vip = false.
Myth vs fact
Myth
The ternary is just a shorter if/else.
Fact
It's an expression with its own typing rules. An if/else that assigns an Integer or a double doesn't promote or unbox the way ?: does.
Myth
Both sides are evaluated and one is thrown away.
Fact
Only the chosen side is evaluated. The other side never runs.
Myth
The ternary is faster than if/else.
Fact
Both compile to the same kind of conditional jump. Pick whichever reads better.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
JLS 15.25 classifies conditionals as boolean, numeric or reference conditional expressions. Numeric ones apply binary numeric promotion (with unboxing) to the two operands; that's why
true ? Integer.valueOf(1) : Double.valueOf(2)yields1.0. Cast one operand toObjectto force a reference conditional. - ▸
A ternary whose operands are compile-time constants is itself a constant expression:
static final int SIZE = DEBUG ? 10 : 1000;is folded byjavac, and can even be used as acaselabel. - ▸
In a poly context (assignment to a declared type, method argument), reference conditionals since Java 8 take their target type from the context, so
List<String> l = flag ? new ArrayList<>() : List.of();infers the diamond from the target. - ▸
Code style: many teams allow one ternary per expression and forbid nesting. Static analysers (Sonar rule S3358) flag nested ternaries for this reason.
Remember this
- 1
The syntax is
condition ? valueIfTrue : valueIfFalse. It's called ternary because it takes three operands, and it's the only operator in Java that does. Its official name is the conditional operator. - 2
It's an expression, not a statement: it produces a value you can store, print, pass or return, as in
String label = age >= 18 ? "adult" : "minor";. Anifstatement produces nothing; it only runs code. You can't use a ternary on its own line just to call methods:x > 0 ? a() : b();is the compile errornot a statement. - 3
Only one of the two value operands is evaluated.
n != 0 ? total / n : 0never divides by zero, because the division only runs when the condition is true. - 4
The result has a single type, worked out at compile time from both operands. Two numbers of different types are promoted (
true ? 1 : 2.0is1.0), and a mix of a wrapper and a primitive (Integerandint) is unboxed, which throwsNullPointerExceptionif the wrapper isnull. - 5
The
?:operator has very low precedence (only assignment and lambdas are lower), so"Age: " + age >= 18 ? ...groups as("Age: " + age) >= 18 ? ...and fails to compile. Wrap a ternary in round brackets whenever it sits inside a bigger expression. - 6
Ternaries nest and are right-associative:
a ? x : b ? y : zmeansa ? x : (b ? y : z). One level is clear; two or more is usually better as an if/else ladder or a switch expression (Topic 2.4).
Explain it without notes
What's the difference between the conditional operator and an if statement?
Why does System.out.println(true ? 1 : 2.0); print 1.0?
How can a ternary throw a NullPointerException on a line with no method calls?
Why do you usually need brackets around a ternary inside a string concatenation?
Practice
Given int hour = 14, print Good morning before 12 and Good afternoon otherwise, using one ternary inside println.
Print the absolute value of -9 and 5 with a ternary (no Math.abs).
Given int n = 0, print n is even or n is odd using a ternary inside the concatenation, then repeat for n = 7.
Trade-offs
- ↔
A ternary is clearest for one simple choice of value. For choices among many values, a switch expression (Topic 2.4) reads better than nested ternaries.
- ↔
Mixing wrappers and primitives in a ternary is concise but hides unboxing. When
nullis possible, an explicit if/else is safer and easier to review.
Done when you can
Done when you can rewrite a value-choosing if/else as a ternary, and back again.
Done when you can explain why a ternary can't be used as a statement.
Done when you can predict the result type of
true ? 1 : 2.0andfalse ? 'a' : 98.Done when you can spot the hidden unboxing NullPointerException in
cond ? 0 : someInteger.Done when you add brackets around a ternary inside any larger expression without thinking.