Switch-выражения в Java
Современные switch-выражения в Java: синтаксис стрелок, yield и исчерпывающее сопоставление с образцом.
В Java 14 switch-выражения стали стандартной возможностью языка. Они дополняют традиционный оператор switch тремя важными улучшениями: возвращают значение, используют более чистый синтаксис стрелок и исключают сквозное выполнение. Если вы работаете с Java 14 или новее — предпочитайте switch-выражения.
В этой главе рассматриваются синтаксис стрелок, ключевое слово yield для блочных тел, проверка исчерпанности и сопоставление с образцом по типу, ставшее стандартным в Java 21. После её прочтения вы будете знать, когда стоит использовать switch-выражение вместо цепочки if/else.
Оператор vs. выражение
Традиционный switch — это оператор: он выполняет действия (побочные эффекты), но не возвращает значения. Switch-выражение вычисляется до значения, которое можно присвоить переменной, вернуть из метода или передать в другой метод. Это единственное отличие определяет всё остальное на этой странице — поскольку результат используется, компилятор может потребовать, чтобы каждая ветка возвращала значение и ни один входной вариант не остался необработанным.
Синтаксис стрелок
String day = "TUE";
String label = switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> "weekday";
case "SAT", "SUN" -> "weekend";
default -> "unknown";
};На что стоит обратить внимание:
- Весь
switchявляется выражением — он вычисляется до значения, которое можно присвоить (обратите внимание на завершающий;). - Каждый case использует
->вместо:. Правая часть — одно выражение или оператор. - В одном case допускается несколько меток через запятую. Никаких
breakи сквозного выполнения.
Блочные тела с yield
Если в ветке нужно несколько операторов, используйте блок — и внутри блока возвращайте значение с помощью yield:
int score = 78;
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> {
System.out.println("close to B");
yield "C";
}
case 6 -> "D";
default -> "F";
};yield аналогичен return, но для switch-выражения — он задаёт значение этого switch и завершает ветку. (Не путайте с return, который завершил бы весь метод.)
yield нужен только внутри блока ({ ... }). В однострочной ветке, например case 8 -> "B";, значение возвращается напрямую, поэтому добавление yield там было бы ошибкой компиляции.
yield — это контекстное ключевое слово: оно действует как ключевое слово только внутри блока switch. Код, в котором yield использовался как имя переменной или метода до Java 14, по-прежнему компилируется, поэтому переход на switch-выражения не ломает существующие идентификаторы.
Операторы по-прежнему работают
Синтаксис стрелок можно использовать и для побочных эффектов — в таком случае это оператор, а не выражение:
switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> System.out.println("weekday");
case "SAT", "SUN" -> System.out.println("weekend");
default -> System.out.println("unknown");
}Преимущество перед старым синтаксисом: не нужен break, нет ошибок сквозного выполнения, плюс несколько меток в одном case.
Исчерпанность
Когда switch-выражение присваивает значение переменной, компилятор требует, чтобы каждый возможный входной вариант давал значение. Для enum это означает либо покрытие всех констант, либо наличие default:
enum Status { PENDING, ACTIVE, DONE }
Status s = Status.ACTIVE;
String label = switch (s) {
case PENDING -> "waiting";
case ACTIVE -> "running";
case DONE -> "complete";
}; // no default needed — all enum values are coveredЕсли позже добавить новую константу в enum и забыть обновить switch, компилятор сообщит об этом. Это надёжная гарантия корректности, которой нет при использовании if/else.
Сопоставление с образцом (Java 21+)
В Java 21 сопоставление с образцом для switch стало стандартной функцией. Теперь можно переключаться по типу значения и привязывать его к типизированной переменной внутри ветки:
Object o = 42;
String description = switch (o) {
case Integer i when i < 0 -> "negative int: " + i;
case Integer i -> "non-negative int: " + i;
case String s -> "string of length " + s.length();
case null -> "null value";
default -> "something else";
};Условие when — это охрана: оно уточняет ветку булевым условием. Такой подход заменяет многие цепочки instanceof и естественно сочетается с более широкими возможностями сопоставления с образцом.
Два важных правила:
- Традиционный
switchвыбрасываетNullPointerExceptionприnull-селекторе. Switch с образцами может включать явныйcase null— без негоnullпо-прежнему приводит к исключению. Теперь незаметно пропуститьnullв switch невозможно. - Ветки проверяются сверху вниз, поэтому располагайте их от наиболее конкретных к наиболее общим. Охраняемый
case Integer i when i < 0должен стоять перед простымcase Integer i, иначе неохраняемая ветка поглотит все целые числа первой.
Практический пример
Что дальше
Мы рассмотрели условные конструкции. Далее — циклы: цикл while — простейший из них.