JavaRush /Курсы /JAVA 25 SELF /Pattern Matching в switch (Java 17/21+)

Pattern Matching в switch (Java 17/21+)

JAVA 25 SELF
65 уровень , 2 лекция
Открыта

1. Классический switch: ограничения прошлого

Давайте вспомним, каким был switch в Java до появления pattern matching. Он был, скажем честно, довольно консервативным. Работал только с примитивами (int, char, и чуть позже enum, String), а если вы хотели обработать разные типы объектов — приходилось писать длинные if-else.

Object obj = ...;
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("Строка: " + s);
} else if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    System.out.println("Число: " + i);
} else if (obj == null) {
    System.out.println("null!");
} else {
    System.out.println("Что-то другое");
}

Удобно? Не очень. Код громоздкий, легко ошибиться, а если добавится новый тип — снова придётся переписывать цепочку if-else.

2. Pattern Matching в switch: революция в Java 17/21+

Java 17 (preview) и окончательно в Java 21+ представили новую версию switch, которая поддерживает сопоставление с образцом (pattern matching). Теперь switch может работать не только с примитивами, но и с любыми объектами, типами, даже с null, и поддерживает дополнительные условия (guard-выражения).

Новый синтаксис: лаконичность и мощь

Пример простого pattern matching в switch:

Object obj = ...;
switch (obj) {
    case String s -> System.out.println("Строка: " + s);
    case Integer i -> System.out.println("Число: " + i);
    case null -> System.out.println("null!");
    default -> System.out.println("Что-то другое");
}

Что здесь происходит?

  • Если obj — строка, переменная s автоматически приводится к типу String.
  • Если obj — целое число, переменная i становится Integer.
  • Если objnull, сработает специальная ветка.
  • Всё остальное — default.

Почему это удобно?

  • Нет ручного приведения типа: Java сама заботится о безопасности типов.
  • Меньше кода: Никаких лишних скобок, кастов и дублирования.
  • Безопаснее: Компилятор проверяет, что вы не забыли обработать возможные варианты (особенно с sealed-классами, о которых поговорим позже).
  • Легко добавлять новые типы: Просто добавьте ещё одну ветку case.

3. Pattern Matching в switch: базовые примеры

Пример 1: Обработка разных типов

Object obj = 42;

switch (obj) {
    case String s -> System.out.println("Строка: " + s);
    case Integer i -> System.out.println("Число: " + i);
    case null -> System.out.println("Это null");
    default -> System.out.println("Неизвестный тип");
}

Вывод:

Число: 42

Пример 2: Использование guard-выражений (when)

Иногда мало просто узнать тип — хочется добавить дополнительное условие.

Object obj = "Hello, world!";

switch (obj) {
    case String s when s.length() > 10 -> System.out.println("Длинная строка: " + s);
    case String s -> System.out.println("Короткая строка: " + s);
    case Integer i when i > 0 -> System.out.println("Положительное число: " + i);
    case Integer i -> System.out.println("Неположительное число: " + i);
    default -> System.out.println("Что-то другое");
}

Вывод:

Длинная строка: Hello, world!

Обратите внимание:

  • when — это guard-выражение, дополнительная проверка для case.
  • Если условие не выполняется, проверяются следующие case.

Пример 3: Обработка null

Object obj = null;

switch (obj) {
    case null -> System.out.println("Это null!");
    default -> System.out.println("Не null");
}

Вывод:

Это null!

4. Pattern Matching в switch с классами и наследованием

Pattern matching в switch особенно хорошо работает с иерархиями классов.

sealed interface Shape permits Circle, Rectangle, Square {}

final class Circle implements Shape {
    final double radius;
    Circle(double radius) { this.radius = radius; }
}

final class Rectangle implements Shape {
    final double width, height;
    Rectangle(double width, double height) { this.width = width; this.height = height; }
}

final class Square implements Shape {
    final double side;
    Square(double side) { this.side = side; }
}
Shape shape = new Rectangle(2, 3);

String description = switch (shape) {
    case Circle c -> "Окружность радиусом " + c.radius;
    case Rectangle r -> "Прямоугольник " + r.width + "x" + r.height;
    case Square s -> "Квадрат со стороной " + s.side;
};
System.out.println(description);

Вывод:

Прямоугольник 2.0x3.0

Фишка: Если вы используете sealed-классы и обработали все их варианты, ветка default не требуется — компилятор понимает, что других вариантов быть не может!

5. Использование switch с record-классами и вложенными паттернами

Pattern matching в switch поддерживает работу с record-классами и вложенными паттернами (начиная с Java 21+).

record Point(int x, int y) {}
record Line(Point start, Point end) {}

Object obj = new Line(new Point(1, 2), new Point(3, 4));

String result = switch (obj) {
    case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
        "Линия от (" + x1 + "," + y1 + ") до (" + x2 + "," + y2 + ")";
    case Point(int x, int y) -> "Точка (" + x + "," + y + ")";
    default -> "Неизвестный объект";
};
System.out.println(result);

Вывод:

Линия от (1,2) до (3,4)

6. Особенности и ограничения pattern matching в switch

Переменные доступны только внутри case

Переменная, объявленная в паттерне (например, String s), существует только внутри соответствующего блока case. За его пределами она недоступна.

switch (obj) {
    case String s -> System.out.println(s); // тут s видно
    // System.out.println(s); // тут ошибка компиляции!
}

Обработка null

  • В старом switch на объектах попытка передать null приводила к NullPointerException.
  • В новом pattern matching switch можно явно обработать null через case null.

Guard-выражения (when)

Дополнительные условия (when) доступны только в expression-style switch (стрелочный синтаксис ->).

case String s when s.isEmpty() -> System.out.println("Пустая строка");

Требования к JDK и IDE

  • Pattern matching в switch — это фича Java 17 (preview) и Java 21+ (стабильно).
  • Для работы нужен JDK 21 и IDE, поддерживающая новые возможности (например, IntelliJ IDEA 2023+).

7. Применение pattern matching switch в реальном приложении

Рассмотрим учебное приложение — простой таск-трекер. Допустим, у вас есть абстрактный тип задачи и разные её виды:

sealed interface Task permits SimpleTask, DeadlineTask, MeetingTask {}

final class SimpleTask implements Task {
    final String description;
    SimpleTask(String description) { this.description = description; }
}

final class DeadlineTask implements Task {
    final String description;
    final java.time.LocalDate deadline;
    DeadlineTask(String description, java.time.LocalDate deadline) {
        this.description = description;
        this.deadline = deadline;
    }
}

final class MeetingTask implements Task {
    final String topic;
    final java.time.LocalTime time;
    MeetingTask(String topic, java.time.LocalTime time) {
        this.topic = topic;
        this.time = time;
    }
}

Вся логика в одном компактном switch:

Task task = new DeadlineTask("Сдать проект", java.time.LocalDate.of(2024, 7, 1));

String info = switch (task) {
    case SimpleTask st -> "Обычная задача: " + st.description;
    case DeadlineTask dt -> "Задача с дедлайном: " + dt.description + ", до " + dt.deadline;
    case MeetingTask mt -> "Встреча: " + mt.topic + " в " + mt.time;
};
System.out.println(info);

Вывод:

Задача с дедлайном: Сдать проект, до 2024-07-01

8. Типичные ошибки при использовании pattern matching в switch

Ошибка №1: забыли обработать null. Если вы не добавили case null, а obj может быть null — получите MatchException (или NullPointerException в старых switch). Всегда явно обрабатывайте null, если это возможно.

Ошибка №2: не учли все наследники sealed-класса. Если вы используете sealed-класс и не обработали все варианты в switch, компилятор выдаст ошибку или потребует ветку default. Не забывайте добавлять новые case при расширении иерархии.

Ошибка №3: переменная pattern недоступна вне блока case. Переменная, объявленная в паттерне, существует только внутри стрелочного блока. Попытка использовать её вне этого блока вызовет ошибку компиляции.

Ошибка №4: использование новых фич на старой JDK или в старой IDE. Pattern matching в switch требует Java 21+ и соответствующей поддержки в вашей IDE. Если используете старую версию — получите ошибку компиляции или некорректные подсказки.

Ошибка №5: путаница с guard-выражениями. Пытаетесь использовать when в старом блочном switch? Не выйдет. Гарды доступны только для стрелочного синтаксиса ->.

Ошибка №6: забыли default-ветку при работе с открытыми иерархиями. Если ваш switch работает не только с sealed-классами, обязательно добавляйте default — иначе не все варианты будут обработаны.

1
Задача
JAVA 25 SELF, 65 уровень, 2 лекция
Недоступна
Классификатор чисел для системы аналитики 🔢
Классификатор чисел для системы аналитики 🔢
1
Задача
JAVA 25 SELF, 65 уровень, 2 лекция
Недоступна
Умная система идентификации питомцев для ветеринарной клиники 🐕🐈🦜
Умная система идентификации питомцев для ветеринарной клиники 🐕🐈🦜
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ