Conditional Operator
The ternary operator ?: packs a whole if-else into a single expression that produces a value — concise for simple choices, but a readability risk when nested or overused.
Java's conditional operator, written condition ? valueIfTrue : valueIfFalse, is the language's only ternary (three-operand) operator. It evaluates condition and yields one of two values as the result of the expression itself, making it a compact alternative to a full if-else statement whenever what you actually want is a value, not a branch of statements.
Syntax: condition ? value1 : value2
The conditional operator has the form condition ? expr1 : expr2. condition must be a boolean expression. If it's true, the whole expression evaluates to expr1; if false, it evaluates to expr2. Exactly one of the two branches is evaluated, never both.
int a = 7, b = 12;
int max = (a > b) ? a : b;
System.out.println(max); // 12It's an Expression, Not a Statement
Unlike if-else, which is a statement that controls which block of code runs, condition ? a : b is an expression that produces a value. This means it can appear anywhere a value is expected: on the right side of an assignment, as a method argument, inside a larger expression, or as part of a String concatenation.
It cannot replace if-else in every situation — if-else can execute multiple statements per branch and doesn't need to produce a value at all; ?: can only ever produce a single value from a single expression on each side.
int n = 7;
System.out.println("n is " + (n % 2 == 0 ? "even" : "odd"));
int absValue = (n < 0) ? -n : n;
printResult(score >= 60 ? "pass" : "fail"); // used directly as an argumentEquivalent if-else Form
Every simple ?: expression that just assigns one of two values can be rewritten as an if-else, and vice versa — the ternary form is syntactic convenience for the value-producing case, not a fundamentally different capability.
// Ternary
int max = (a > b) ? a : b;
// Equivalent if-else
int max;
if (a > b) {
max = a;
} else {
max = b;
}Type Rules in Practice
The compiler determines a single result type for the whole ?: expression from its two branches. When both branches are the same type, that's the result type. When the branches are different numeric types, Java applies binary numeric promotion rules similar to arithmetic operators, which can produce a surprising result type (and, for mixed primitives, even a surprising value due to unboxing/widening).
Mixing a primitive and its boxed wrapper type across the two branches (e.g. int and Integer) can trigger unboxing of the wrapper, which throws a NullPointerException at runtime if that wrapper value is null — even though the branch that would have used the null value was never the one "chosen" logically.
int i = 5;
double d = 2.5;
// The condition doesn't matter for the TYPE - both branches are considered
var result = true ? i : d; // result is double: 5.0 (i is widened to double)
System.out.println(result);Common Type Pitfall: Mixed Wrapper and Primitive
This happens because when one branch is int and the other is Integer, the compiler picks int as the result type of the whole expression, which requires unboxing the Integer branch — and that unboxing happens as part of establishing the expression's type, independent of which branch is logically chosen at runtime in this particular case. Avoid mixing primitive and wrapper types across a ternary's branches, or ensure the wrapper is guaranteed non-null.
Integer maybeNull = null;
int fallback = 0;
boolean useFallback = true;
// DANGER: both branches are examined for typing purposes,
// forcing unboxing of maybeNull even when useFallback is true
int result = useFallback ? fallback : maybeNull; // NullPointerException!Chaining and Nested Ternaries
The right-hand branch of a ?: can itself be another ?:, letting you chain several conditions into one expression — but nesting more than one or two levels quickly becomes hard to read.
Ternaries associate right-to-left, so a ? b : c ? d : e parses as a ? b : (c ? d : e), which is what makes this chained pattern work as a grade-ladder.
int score = 85;
String grade = (score >= 90) ? "A"
: (score >= 80) ? "B"
: (score >= 70) ? "C"
: "F";
System.out.println(grade); // BWhen to Use, and When to Avoid
Use ?: for short, simple value choices — picking between two values for an assignment, a return statement, or a print argument. Avoid it when either branch has side effects beyond producing a value, when the logic needs more than a simple two-way value choice, or when nesting would require more than one level to stay readable. In those cases, a full if-else (or a switch expression) communicates the logic more clearly.
// Good use: simple value selection
String label = (count == 1) ? "item" : "items";
// Poor use: side effects buried inside a ternary
(isValid) ? saveRecord() : logError(); // works, but reads like a hidden if-else - prefer an actual if-else herePractical Usage
Common everyday uses: picking a singular/plural word, clamping a value, providing a default when something might be zero/empty, and building formatted strings.
int count = 3;
System.out.println(count + " " + (count == 1 ? "apple" : "apples"));
int value = -5;
int clamped = (value < 0) ? 0 : value;
String name = (input != null && !input.isEmpty()) ? input : "Anonymous";Edge Cases
Only the chosen branch's expression is evaluated — the other one is never executed, so it's safe to put an expression that would otherwise throw or have unwanted side effects in the branch that won't be taken.
Operator precedence: ?: has very low precedence, lower than most operators including arithmetic and relational ones, but it is right-associative and typically needs parentheses around the whole expression when embedded inside another expression (like string concatenation) for clarity, even when not strictly required by precedence rules.
int[] arr = {1, 2, 3};
int index = 5;
// Safe: arr[index] is never evaluated because the condition is false
int value = (index < arr.length) ? arr[index] : -1;
System.out.println(value); // -1, no ArrayIndexOutOfBoundsExceptionComplete Example
A chained ternary expresses the classic FizzBuzz rules as a single value-producing expression assigned once per loop iteration.
public class FizzBuzzTernary {
public static void main(String[] args) {
for (int i = 1; i <= 15; i++) {
String output = (i % 15 == 0) ? "FizzBuzz"
: (i % 3 == 0) ? "Fizz"
: (i % 5 == 0) ? "Buzz"
: String.valueOf(i);
System.out.println(output);
}
}
}Mental Model
Read condition ? a : b as "if condition, the value of this whole expression is a; otherwise, it's b." It's an if-else that hands back a value instead of choosing which statements to run — think of it as a value-level fork, not a control-flow fork.
Quick Reference
condition ? valueIfTrue : valueIfFalse
- an expression, not a statement: produces a value
- exactly one branch is evaluated, never both
- both branches contribute to determining the overall result type
- right-associative, enabling condition1 ? a : condition2 ? b : c chains
- use for simple value choices; use if-else for anything with side effects
or more than one level of real nestingKey Takeaways
condition ? value1 : value2 is an expression that yields value1 when condition is true and value2 when it's false.
Only the chosen branch is actually evaluated at runtime, so it's safe to guard against errors like out-of-bounds access.
The compiler still considers both branches' types together to determine the whole expression's type, which can force unwanted unboxing/widening even in the branch not taken.
Chained ternaries associate right-to-left and can express multi-way choices, but more than one or two levels usually hurts readability.
Prefer it for simple value selection; prefer if-else or switch when a branch needs side effects or the logic is more than a two-way choice.
int result = flag ? doSomething(): doSomethingElse(); // both are void if (flag) { doSomething(); } else { doSomethingElse(); }
The conditional operator requires both branches to produce a value; if the methods return void, this doesn't compile, and even when it does compile, using ?: purely for its side effects reads like a disguised if-else.
Integer count = null; int total = (someFlag) ? 0: count; // NPE risk even when someFlag is true int total = (someFlag) ? 0: (count != null ? count: 0);
Mixing an int branch with an Integer branch forces the compiler to establish int as the result type, which unboxes the Integer branch as part of typing the expression — this can throw NullPointerException even on runs where the null branch wasn't logically selected in a simpler two-branch case, so guard against null explicitly.
String result = a > b ? "bigger" : c > d ? "also bigger" : e > f ? "still bigger" : "smallest";
String result; if (a > b) { result = "bigger"; } else if (c > d) { result = "also bigger"; } else if (e > f) { result = "still bigger"; } else { result = "smallest"; }
Three levels of chained ternaries packed onto one line are hard to scan and easy to misparse; an if-else-if chain communicates the same multi-way branch far more clearly at this depth.
int x = condition ? 5: "five"; // mismatched types String x = condition ? "5": "five";
Both branches of a ternary must be types the compiler can reconcile to a single result type; mixing an int literal with a String literal has no common result type, so this fails to compile.
Is condition ? a : b a statement or an expression, and why does that distinction matter?
It's an expression — it produces a value. That matters because it can be used anywhere a value is expected (assignments, method arguments, string concatenation), unlike if-else, which is a statement that controls which block of code executes.
How many of the two branches in condition ? a : b are actually evaluated at runtime?
Exactly one — whichever branch corresponds to the condition's boolean result. The other branch's expression is never evaluated, which is why it's safe to put something that could throw (like an out-of-bounds array access) in the branch that won't be taken.
Why can mixing an int branch and an Integer branch in a ternary cause a NullPointerException even when the Integer branch isn't the one logically selected?
The compiler determines the ternary's overall result type by considering both branches together; when one is int and the other is Integer, it settles on int as the result type, which requires unboxing the Integer branch as part of establishing that type — and unboxing a null Integer throws NullPointerException, regardless of which branch's value ultimately gets used.
How do chained ternaries like a ? b : c ? d : e associate, and what does that mean for how they're parsed?
Right-to-left: it parses as a ? b : (c ? d : e), which is what allows a chain of conditions to act like a ladder of if-else-if branches, each producing a value.
Give a rule of thumb for when to prefer if-else over the conditional operator.
Prefer if-else when a branch needs to execute a side effect (not just produce a value), when there's more than a simple two-way choice, or when a chain of ternaries would need more than one or two levels to express — at that point if-else or switch communicates the logic more clearly.
What must be true about the two branches of a ternary for it to compile?
The compiler must be able to determine a single common result type for the expression from the two branches' types — if there's no way to reconcile them (like a numeric literal versus a String literal), it's a compile error.
Rewrite String label = (count == 1) ? "item" : "items"; as an equivalent if-else.
String label; if (count == 1) { label = "item"; } else { label = "items"; }
Write a single ternary expression that assigns "positive", "negative", or you may assume the value is never zero, to a String variable based on an int n.
int n = -8; String sign = (n > 0) ? "positive" : "negative"; System.out.println(sign);
The condition n > 0 selects "positive" when true and "negative" when false, covering both non-zero cases in one expression.
What does this print?
equal — the first condition a == b is true (both are 4), so the whole chained ternary short-circuits to "equal" without ever evaluating the second condition.
Write a ternary expression that safely gets the last character of a String s, or returns '?' if s is null or empty.
String s = null; char result = (s == null || s.isEmpty()) ? '?' : s.charAt(s.length() - 1); System.out.println(result);
The condition checks for null or empty first; because Java short-circuits && and ||, s.isEmpty() is only evaluated when s is not null, and s.charAt(...) is only evaluated in the false branch when s is guaranteed non-null and non-empty, avoiding a NullPointerException or StringIndexOutOfBoundsException.
What happens when this runs, and why?
It throws a NullPointerException. Even though the condition is true and "logically" only the 10 branch should matter, the compiler must unify the result type of int (10) and Integer (boxed) into a single type (int), which requires unboxing boxed as part of the expression's typing — and unboxing null always throws, regardless of which branch's value is ultimately used.
int a = 4, b = 4;
String result = (a == b) ? "equal" : (a > b) ? "a bigger" : "b bigger";
System.out.println(result);Integer boxed = null;
int x = true ? 10 : boxed;
System.out.println(x);