Тернарный оператор в Java
Тернарный оператор ?: (в спецификации Java он называется условным оператором, conditional operator) — единственный оператор языка с тремя операндами, отсюда и название «тернарный». Он выбирает одно из двух значений в зависимости от условия и часто используется вместо короткого оператора if-else. Главное отличие: тернарный оператор — это выражение, то есть он возвращает значение, которое можно сразу присвоить переменной, передать в метод или вернуть из метода.
Синтаксис тернарного оператора
Общая форма тернарного оператора:
условие ? выражение1 : выражение2 Условие — любое выражение, вычисление которого даёт логическое значение типа boolean (например, сравнение или комбинация логических операторов).
Если условие истинно (true), вычисляется выражение1 и его значение становится результатом всей операции; в противном случае вычисляется выражение2. Важно: вычисляется только одна из двух ветвей — вторая не выполняется вовсе.
Выражение1 и выражение2 должны возвращать значения одинакового или совместимого типа, и этим типом не может быть void.
Важно
Тернарный оператор — это выражение, а не оператор потока управления. Он обязан вернуть значение, поэтому запись вида условие ? doThis() : doThat(); отдельной строкой не скомпилируется, если методы возвращают void. Когда нужно просто выполнить одно из двух действий, используйте if-else.
Пример использования
Классический пример — получение абсолютного значения числа: если i отрицательно, берём -i, иначе само i:
public class TernaryOperationExample1 {
public static void main(String[] args) {
int i, k;
i = -10;
k = i < 0 ? -i : i; // абсолютное значение переменной i
System.out.print("Абсолютное значение ");
System.out.println(i + " равно " + k); // Абсолютное значение -10 равно 10
}
} Тот же результат с if-else занял бы четыре строки. Ещё несколько типичных применений — в аргументе метода и в return:
int a = 7, b = 12;
System.out.println(a > b ? "a больше" : "b больше или равно"); // в аргументе метода
int max(int x, int y) {
return x > y ? x : y; // в операторе return
} Тернарный оператор или if-else: что выбрать
Тернарный оператор и if-else не взаимозаменяемы полностью — у каждого своя область применения:
| Ситуация | Что использовать | Почему |
|---|---|---|
Выбор одного из двух значений при присваивании или в return | Тернарный оператор | Короче и читается как единое выражение: k = i < 0 ? -i : i; |
| Выполнение действий (вывод, изменение состояния), а не выбор значения | if-else | Тернарный оператор обязан вернуть значение и не может быть отдельной инструкцией с void-ветвями |
| В ветвях несколько инструкций | if-else | Каждая ветвь тернарного оператора — ровно одно выражение |
| Три и более вариантов выбора | if-else if или switch-выражение | Цепочки вложенных тернарников трудно читать |
Вложенные тернарные операторы
Ветвью тернарного оператора может быть другой тернарный оператор. Оператор правоассоциативен: выражение a ? b : c ? d : e вычисляется как a ? b : (c ? d : e), поэтому цепочка без скобок читается сверху вниз, как if-else if:
public class NestedTernaryExample {
public static void main(String[] args) {
int score = 74;
String grade = score >= 90 ? "отлично"
: score >= 70 ? "хорошо"
: score >= 50 ? "удовлетворительно"
: "неудовлетворительно";
System.out.println(grade); // хорошо
}
} Один уровень вложенности с таким «лесенкой»-форматированием ещё допустим, но более глубокие или расставленные в произвольном порядке вложенные тернарники быстро превращаются в головоломку. Начиная с Java 14 для выбора из нескольких вариантов удобнее switch-выражение, которое, как и тернарный оператор, возвращает значение:
String size = switch (code) {
case 1 -> "S";
case 2 -> "M";
default -> "L";
}; Типичные ловушки
Две особенности тернарного оператора регулярно встречаются в вопросах на собеседованиях.
1. Числовое расширение типов (numeric promotion). Если ветви имеют разные числовые типы, результат приводится к общему типу до выбора ветви:
Object result = true ? 1 : 2.0;
System.out.println(result); // 1.0, а не 1! Хотя условие истинно и выбрана ветвь 1, типом всего выражения является double (общий тип для int и double), поэтому 1 превращается в 1.0.
2. NullPointerException при автораспаковке. Если одна ветвь имеет тип-обёртку (Integer), а другая — примитив (int), типом выражения становится примитив, и обёртка автоматически распаковывается. Если в ней null — программа падает:
Integer count = null;
boolean flag = true;
int r = flag ? count : 0; // NullPointerException: count распаковывается в int Важно
Тип результата тернарного оператора определяется обоими ветвями на этапе компиляции, а не той ветвью, которая фактически выполнится. Смешивание int и double даёт double; смешивание Integer и int даёт int с автораспаковкой — и риском NullPointerException. Держите обе ветви одного типа.
Стиль и читаемость
Несколько практических рекомендаций:
- Используйте тернарный оператор для коротких выборов одного из двух значений; как только выражение перестаёт помещаться в одну строку и читаться с первого взгляда — переходите на
if-else. - Избегайте побочных эффектов в ветвях (вызовов методов, меняющих состояние): читающий код ожидает от тернарного оператора простого выбора значения.
- Не вкладывайте тернарные операторы глубже одного уровня; для цепочек условий берите
if-else ifили switch-выражение. - При сомнениях в приоритете операторов заключайте условие в скобки:
(a > b) ? a : b— это ничего не меняет для компилятора, но помогает читателю.
Часто задаваемые вопросы
Чем тернарный оператор отличается от if-else в Java?
Тернарный оператор — это выражение: он возвращает значение, которое можно присвоить переменной, передать в метод или вернуть через return. Оператор if-else — инструкция потока управления: он выполняет действия, но сам ничего не возвращает. По скорости они эквивалентны — выбор определяется читаемостью, а не производительностью.
Можно ли вкладывать тернарные операторы друг в друга?
Да, ветвью тернарного оператора может быть другой тернарный оператор. Оператор правоассоциативен: a ? b : c ? d : e означает a ? b : (c ? d : e) и читается как цепочка if-else if. Однако глубокая вложенность плохо читается — для трёх и более вариантов лучше использовать if-else if или switch-выражение (Java 14+).
Почему выражение true ? 1 : 2.0 возвращает 1.0, а не 1?
Тип результата тернарного оператора вычисляется компилятором из типов обеих ветвей, а не той, что реально выполнится. Для int и double общим типом является double, поэтому значение 1 расширяется до 1.0. Чтобы избежать сюрпризов, делайте обе ветви одного типа.
Может ли тернарный оператор выбросить NullPointerException?
Да. Если одна ветвь имеет тип-обёртку, например Integer, а другая — примитив int, типом всего выражения становится примитив, и значение обёртки автоматически распаковывается. Если в ней хранится null, распаковка приводит к NullPointerException, даже когда условие выглядит безобидно. Решение — привести обе ветви к одному типу.
Презентацию с видео можно скачать на Patreon.
Video Explanation
Prefer video format? Watch this lesson with examples and explanations.
Comments