W3docs

Современные выражения switch в Java

Используйте современные выражения switch в Java: стрелочные метки, yield, полнота покрытия и сопоставление с образцом.

Традиционный оператор switch существует в Java с версии 1.0, но нёс немало проблем: ошибки из-за проваливания, повторяющиеся операторы break и отсутствие возможности возвращать значение. Выражения switch, финализированные в Java 14, устраняют все эти недостатки. Они превращают switch из неуклюжего оператора управления потоком в лаконичное выражение, возвращающее значение.

В этой главе рассматривается современный switch: стрелочные метки, ключевое слово yield, многозначные метки case, проверка полноты покрытия и принципиальные отличия новой формы от привычного оператора.

От оператора к выражению

Классический оператор switch выполняет побочные эффекты и требует break для предотвращения проваливания. Забудете break — выполнение молча перейдёт к следующему case, что является известным источником ошибок.

// Traditional switch statement (error-prone)
String kind;
switch (day) {
    case SATURDAY:
    case SUNDAY:
        kind = "weekend";
        break;          // forget this and you fall through
    default:
        kind = "weekday";
}

Выражение switch сводит всё это к одному присваиванию. Форма со стрелкой (->) никогда не проваливается, поэтому break не нужен.

// Modern switch expression
String kind = switch (day) {
    case SATURDAY, SUNDAY -> "weekend";
    default               -> "weekday";
};

Весь switch теперь вычисляется как значение, которое можно присвоить, вернуть или передать в качестве аргумента напрямую.

Стрелочные метки и многозначные case

Стрелочная метка case L -> связывает одну метку (или несколько, разделённых запятой) с единственным действием. Выполняется только подходящая ветка — проваливания не происходит.

int numLetters = switch (month) {
    case JANUARY, JUNE, JULY          -> 4;
    case FEBRUARY, MARCH, APRIL, MAY  -> 5;
    case SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> switchOnLength(month);
    case AUGUST                       -> 6;
};

Группировка меток через запятую заменяет старый приём с несколькими пустыми строками case для общего тела и читается значительно понятнее.

ВозможностьТрадиционный switch (case L:)Современный switch (case L ->)
ПроваливаниеДа, если не поставить breakНет, каждая ветка изолирована
Возврат значенияНетДа (это выражение)
Несколько метокНесколько пустых строк caseЧерез запятую в одной строке
Область видимости переменныхОбщая для всего блокаЛокальная для каждого блока ветки

Блоки и ключевое слово yield

Если ветка требует больше одного выражения, используйте блок { ... } и возвращайте значение через yield. Ключевое слово yield для выражения switch — то же, что return для метода: оно задаёт значение, которое производит ветка.

int gradePoints = switch (grade) {
    case 'A' -> 4;
    case 'B' -> 3;
    default -> {
        log("Unknown grade: " + grade);
        yield 0;          // the value this branch evaluates to
    }
};

Можно по-прежнему использовать двоеточные метки с yield, если предпочитаете старый синтаксис, но форма со стрелкой — идиоматичный современный выбор, полностью исключающий случайное проваливание.

Полнота покрытия и default

Выражение switch должно быть исчерпывающим: каждый возможный ввод должен быть обработан, так как выражение обязано возвращать значение в любом случае. Для большинства типов это обеспечивается веткой default. Для enum компилятор может проверить полноту покрытия напрямую — если вы охватываете все константы, default становится необязательным.

// No default needed: all enum constants are covered,
// so the compiler knows the switch is exhaustive.
boolean isWeekend = switch (day) {
    case SATURDAY, SUNDAY -> true;
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
};

Если вы пропустите константу и нет default, код не скомпилируется. Эта защита на этапе компиляции — одно из главных практических преимуществ перед старым оператором, который молча оставлял переменную неинициализированной.

Сопоставление с образцом в switch

В последних версиях Java возможности switch расширены: каждый case может сопоставляться с типом значения, а не только с константой. Это сопоставление с образцом для switch (превью в Java 17–20, финализировано в Java 21). Вместо цепочки проверок instanceof и приведений типов вы помечаете каждую ветку шаблоном типа и привязываете переменную в том же шаге.

static String describe(Object obj) {
    return switch (obj) {
        case Integer i -> "int " + i;            // matches and binds i
        case String s  -> "string of length " + s.length();
        case null      -> "nothing";             // null can be its own label
        default        -> "something else";
    };
}

Сопоставление с образцом особенно мощно при работе с record и запечатанными типами: когда switch охватывает все разрешённые подтипы запечатанного типа, компилятор считает его исчерпывающим и default можно полностью опустить.

sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side)   implements Shape {}

static double area(Shape shape) {
    return switch (shape) {          // no default: all permitted types covered
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Square s -> s.side() * s.side();
    };
}
Примечание
До появления сопоставления с образцом switch мог проверять только enum, целочисленный тип или String. Шаблоны типов позволяют switch работать с любым ссылочным типом, что делает его естественной заменой длинных цепочек if/else if.

Полный рабочий пример

Программа ниже объединяет все элементы: стрелочные метки с многозначными case, блок yield для вычисляемой ветки, исчерпывающее покрытие enum и выражение switch, присвоенное напрямую переменной.

java— editable, runs on the server

Что следует вынести из запуска:

  • kind() возвращает результат выражения switch напрямую — MONDAY и FRIDAY выводят weekday, SATURDAY выводит weekend, и всё это без единого break.
  • Две стрелочные ветки в kind() охватывают все семь констант enum, поэтому switch исчерпывающий и не требует default.
  • В letterGrade() оценки 95, 83, 71 и 64 аккуратно отображаются через стрелочные метки в 4, 3, 2 и 1 балл соответственно.
  • Оценка 42 попадает в блок default, который сначала выводит (failing score 42) через строку с побочным эффектом, а затем возвращает 0 через yield — демонстрируя, как блочная ветка может выполнять работу перед возвратом значения.
  • Финальный switch присваивает TWO прямо в переменную label, доказывая, что выражение switch — это значение, которое можно хранить, а не просто управление потоком.

Когда использовать какую форму

  • Используйте выражение switch (case L ->), когда цель — вычислить и вернуть единственное значение. Оно исчерпывающее, без проваливания и читается как одно присваивание.
  • Традиционный оператор switch по-прежнему уместен, когда каждая ветка является чисто побочным эффектом (логирование, диспетчеризация) и нет значения для возврата.
  • Используйте шаблоны типов, когда вы ветвитесь по типу объекта во время выполнения, особенно по константам enum или подтипам запечатанного типа.

Для полной истории и формы с двоеточием смотрите выражения switch в Java.

Практика

Практика
В современном выражении switch какое ключевое слово возвращает значение из блока (формы ветки { ... })?
В современном выражении switch какое ключевое слово возвращает значение из блока (формы ветки { ... })?
Was this page helpful?