Command Palette

Search for a command to run...

PHASE 2Beginner ~29 min· topic 2 of 10

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 are 3 and 4.
Ternary
Taking three operands. ?: is Java's only ternary operator.
Expression
A piece of code that produces a value, like 2 + 3 or age >= 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 int to double) so two values share a type.
Unboxing
Java turning a wrapper object such as Integer into its plain primitive (int) automatically. Unboxing null throws NullPointerException.

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"'.

Main.javawhole filejava
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 adult

02It'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.

Main.javawhole filejava
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);
terminal
$ java Main.java
── expected output ──
You have 1 item
Bigger: 12

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.

Only one side runsdiagram
Rendering diagram…

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").

Main.javawhole filejava
int age = 20;
System.out.println("Age: " + age >= 18 ? "adult" : "minor");     // compile error
System.out.println("Age: " + (age >= 18 ? "adult" : "minor"));   // Age: adult
terminal
$ javac Main.java
── expected output ──
Main.java:4: error: bad operand types for binary operator '>='
System.out.println("Age: " + age >= 18 ? "adult" : "minor");
^
first type: String
second type: int
1 error

05The 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.

Main.javawhole filejava
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);
    }
}
terminal
$ javac Main.java && java Main
── expected output ──
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "<local1>" is null
at Main.main(Main.java:5)
Compile with `javac -g Main.java` and the message names the variable: `because "bonus" is null`.

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.

Main.javawhole filejava
int n = -4;
String sign = n > 0 ? "positive"
            : n < 0 ? "negative"
            : "zero";
System.out.println(sign);   // negative

Try it yourself

  1. 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. 2

    Rewrite as if/else

    Take the sign line from 'Choosing values with ?:' and rewrite it as an else-if ladder that assigns sign. Which version do you find easier to read? Now add a fourth case, 'big' for n > 100, to both.

  3. 3

    See only one side run

    In 'Only one side is evaluated', change true to 1 > 2. Predict which 'evaluating' line appears, then run.

Code & diagrams

Choosing values with ?: New tab
Sign in to run this example in your browser.

Expected output

Label: minor
Cart: 3 items
Max: 12
Average: 0
Sign: negative
Only one side is evaluated New tab
Sign in to run this example in your browser.

Expected output

condition true:
  evaluating left
  result 1
condition false:
  evaluating right
  result 2
Typing surprises: promotion, char, boxing New tab
Sign in to run this example in your browser.

Expected output

1.0
b
98
1.0 is a Double
1 is an Integer

Break 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");.

terminal
$ javac Main.java
── what you'll see ──
Main.java:4: error: bad operand types for binary operator '>='
System.out.println("Age: " + age >= 18 ? "adult" : "minor");
^
first type: String
second type: int
1 error

Break #2

Use a ternary as a statement

Write x > 0 ? System.out.println("a") : System.out.println("b"); on its own line.

terminal
$ javac Main.java
── what you'll see ──
Main.java:4: error: not a statement
x > 0 ? System.out.println("a") : System.out.println("b");
^
1 error

Break #3

Mix Integer and int when the Integer is null

Write int total = vip ? 100 : bonus; with Integer bonus = null and vip = false.

terminal
$ javac Main.java && java Main
── what you'll see ──
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "<local1>" is null
at Main.main(Main.java:5)

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) yields 1.0. Cast one operand to Object to 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 by javac, and can even be used as a case label.

  • ▸

    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. 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. 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";. An if statement 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 error not a statement.

  3. 3

    Only one of the two value operands is evaluated. n != 0 ? total / n : 0 never divides by zero, because the division only runs when the condition is true.

  4. 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.0 is 1.0), and a mix of a wrapper and a primitive (Integer and int) is unboxed, which throws NullPointerException if the wrapper is null.

  5. 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. 6

    Ternaries nest and are right-associative: a ? x : b ? y : z means a ? 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

01

What's the difference between the conditional operator and an if statement?

02

Why does System.out.println(true ? 1 : 2.0); print 1.0?

03

How can a ternary throw a NullPointerException on a line with no method calls?

04

Why do you usually need brackets around a ternary inside a string concatenation?

Practice

01

Given int hour = 14, print Good morning before 12 and Good afternoon otherwise, using one ternary inside println.

02

Print the absolute value of -9 and 5 with a ternary (no Math.abs).

03

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 null is 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.0 and false ? '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.