W3docs

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";
};

На что стоит обратить внимание:

  1. Весь switch является выражением — он вычисляется до значения, которое можно присвоить (обратите внимание на завершающий ;).
  2. Каждый case использует -> вместо :. Правая часть — одно выражение или оператор.
  3. В одном 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, иначе неохраняемая ветка поглотит все целые числа первой.

Практический пример

java— editable, runs on the server

Что дальше

Мы рассмотрели условные конструкции. Далее — циклы: цикл while — простейший из них.

Практика

Практика
Какое ключевое слово возвращает значение ветки в switch-выражении с блочным телом в стрелочной форме?
Какое ключевое слово возвращает значение ветки в switch-выражении с блочным телом в стрелочной форме?
Was this page helpful?