Many decisions in code do exactly one thing: pick a value. If the score is 50 or more, the label is "Pass"; otherwise it is "Fail". Written out as a full if/else, that is four lines and a throwaway variable for what is really a single choice. Java's ternary operator ?: collapses it into one expression: String label = score >= 50 ? "Pass" : "Fail";. It is the only operator in Java that takes three operands, and because it produces a value it can appear anywhere a value can — inside assignments, method arguments, and return statements. Used well, it strips clutter out of your code. Used carelessly, it packs logic so densely that nobody, including you three weeks later, can read it.
flowchart TD C["condition"] -->|true| T["value if true"] C -->|false| F["value if false"] style C fill:#c2410c,color:#fff
A ternary is an expression, not a statement
An if/else is a statement — it directs the flow of the program but produces no value. A ternary is an expression — it resolves to a value you can use immediately. That is why int max = a > b ? a : b; works, while int max = if (a > b) { a } else { b } does not even compile. Because a ternary yields a value, it can be passed straight into a method call — print(status == OK ? "ready" : "waiting") — or returned directly: return n >= 0 ? n : -n;. The real reason the operator exists is not to save lines; it is to let a choice flow into a place where a statement is simply not allowed.
The three parts, and a precedence surprise
The shape is always condition ? valueIfTrue : valueIfFalse. The condition must evaluate to a boolean. Java evaluates the condition first, then evaluates only one of the two branches — the other is skipped, which matters when a branch has side effects. The result type is the common type of the two branches.
The trap is precedence. ?: binds very loosely — looser than arithmetic, looser than +, looser than comparison. So passed ? "Pass" : "Fail" + "!" does not attach "!" to both branches. The + runs first, so Java reads it as passed ? "Pass" : ("Fail" + "!"). When passed is true, you get plain "Pass" with no exclamation mark. The fix is a habit: whenever a ternary shares a line with + or any other operator, wrap the whole ?: in parentheses.
flowchart TD E["a ternary with a plus on one branch"] --> G["plus binds tighter than the ternary"] G --> P["the plus joins only the false branch"] P --> T["condition true gives the first value alone"] P --> F["condition false gives the second value plus the extra"] style E fill:#c2410c,color:#fff style P fill:#1e293b,color:#fff
public class Main {
public static void main(String[] args) {
int a = 8;
int b = 5;
int max = a > b ? a : b;
System.out.println(max);
int score = 73;
String grade = score >= 60 ? "Pass" : "Fail";
System.out.println(grade);
int n = 7;
String parity = n % 2 == 0 ? "even" : "odd";
System.out.println(parity);
}
}
Compound assignment and its hidden cast
+=, -=, *=, /=, and %= fold an operation into the assignment. x += 5 means x = x + 5. The right side is evaluated completely first, so y *= 2 + 3 becomes y = y * 5, not y = y * 2 + 3.
There is a second, subtler rule: compound assignment silently casts the result back to the variable's type. byte b = 3; b += 200; compiles, because the compiler treats it as b = (byte)(b + 200). The arithmetic 3 + 200 is 203, but a byte only holds the values -128 to 127, so it wraps to -53 — a result you never asked for, delivered with no warning. The long form b = b + 200 would not compile, because b + 200 promotes to int and the compiler refuses to narrow it back without an explicit cast. The compound operator hides that cast from you.
flowchart LR S["x += 5"] --> R["compute x + 5"] R --> C["cast back to the type of x"] C --> Store["store the result in x"] SB["byte b then b += 200"] --> CB["b becomes (byte)(b + 200)"] CB --> W["203 wraps to -53"] style S fill:#c2410c,color:#fff style C fill:#b45309,color:#fff style W fill:#166534,color:#fff
public class Main {
public static void main(String[] args) {
int total = 100;
total += 50;
System.out.println(total);
total -= 30;
System.out.println(total);
total *= 2;
System.out.println(total);
total /= 4;
System.out.println(total);
total %= 10;
System.out.println(total);
}
}
When one line stops being clearer
The ternary shines when both branches are simple values and the choice is obvious — picking a maximum, a label, or a default. The moment a branch calls a method, runs a calculation, or chains into another ternary, switch to a full if/else. A construction like a ? b ? c : d : e is technically legal and practically unreadable, and most style guides ban nested ternaries outright.
The same honesty applies to compound assignment. It is the idiomatic way to update an accumulator (total += price), and clearer than writing the long form. But reach for it only when the operation is tiny; a compound line that silently runs three steps has stopped saving anyone any time.
public class Main {
public static void main(String[] args) {
int score = 75;
String result = score >= 60 ? "Pass" : "Fail";
System.out.println(result);
int x = 10;
x += 5;
System.out.println(x);
int y = 7;
y *= 2 + 3;
System.out.println(y);
byte b = 3;
b += 200;
System.out.println(b);
}
}
What does this program print? Read each ternary carefully and type the exact output (three lines).
public class Main {
public static void main(String[] args) {
int a = 8;
int b = 5;
int max = a > b ? a : b;
System.out.println(max);
String sign = a - b >= 0 ? "non-negative" : "negative";
System.out.println(sign);
int c = 4;
String parity = c % 2 == 0 ? "even" : "odd";
System.out.println(parity);
}
}
a > b is true, so max takes the value after the question mark, which is a.
a - b is 3, which is >= 0, so sign takes the true-branch string.
4 % 2 is 0, so the condition holds and parity takes the even branch.
What does this program print? Read it carefully and type the exact output (four lines).
public class Main {
public static void main(String[] args) {
int score = 75;
String result = score >= 60 ? "Pass" : "Fail";
System.out.println(result);
int x = 10;
x += 5;
System.out.println(x);
int y = 7;
y *= 2 + 3;
System.out.println(y);
byte b = 3;
b += 200;
System.out.println(b);
}
}
The ternary picks the value after ? when the condition is true, otherwise after :.
Compound assignment evaluates the right side first: 2 + 3 = 5, then y = y * 5.
+= on a byte includes an implicit cast back to byte, so 203 wraps to -53.
This looks like it should print Pass!, but + binds tighter than ?:. Predict the exact one-line output.
public class Main {
public static void main(String[] args) {
boolean passed = true;
String label = passed ? "Pass" : "Fail" + "!";
System.out.println(label);
}
}
- has higher precedence than ?:, so the expression groups as passed ? "Pass" : ("Fail" + "!").
passed is true, so the ternary picks the value right after the question mark: "Pass".
The + "!" attaches only to the "Fail" branch, which is never chosen.
What is b += 1; equivalent to when b is a byte?
Compound assignment on a byte, short, or char silently casts the result back to the smaller type. b = b + 1 would fail because b + 1 promotes to int; b += 1 compiles because the compiler inserts the cast for you.
A ternary can choose the value a compound operator adds. Orders of 150 or more get free shipping; smaller orders pay the shipping fee. Predict the exact two-line output.
public class Main {
public static void main(String[] args) {
int total = 100;
int shipping = 12;
total += total >= 150 ? 0 : shipping;
System.out.println(total);
total = 200;
total += total >= 150 ? 0 : shipping;
System.out.println(total);
}
}
First total is 100, which is not >= 150, so the ternary picks shipping (12). total += 12 gives 112.
total is then reset to 200, which is >= 150, so the ternary picks 0. total += 0 leaves it 200.
The ternary is evaluated first, and its chosen value is what += adds.
Recap
- The ternary
cond ? a : bpicksawhencondis true, otherwiseb; only the chosen branch is evaluated. - It is an expression with a value, which is why it can go where an
ifstatement cannot. ?:binds very loosely — parenthesise the whole ternary whenever it shares a line with+or another operator.- Compound assignment (
+=,-=,*=,/=,%=) evaluates the right side fully first, then casts the result back to the variable's type. - That hidden cast lets
b += 200compile on abytebut silently wrap to-53, and it truncates/=on anint.
Next you will put these operators to work inside loops and branches.