JavaRush /Курси /JAVA 25 SELF /Pattern Matching for instanceof

Pattern Matching for instanceof

JAVA 25 SELF
Рівень 65 , Лекція 1
Відкрита

1. Проблема класичного instanceof

Давайте почнемо з невеликої ностальгії. До Java 16, коли потрібно було перевірити тип об’єкта, а потім використати його як цей тип, доводилося писати багатослівний шаблон із перевіркою instanceof і явним приведенням:

Object obj = ...; // якийсь об’єкт

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("Довжина рядка: " + s.length());
}

Нудний, але частий шаблон

Цей шаблон трапляється дуже часто — обробники подій, робота з різнорідними колекціями, бізнес-логіка з успадкуванням тощо.

Чому це незручно

  • Дублювання коду — доводиться двічі вказувати тип: і в instanceof, і в приведенні.
  • Ризик помилки — легко випадково привести до не того типу й отримати ClassCastException.
  • «Шумний» код — погіршує читабельність, особливо за множинних перевірок.

2. Pattern Matching for instanceof: Новий синтаксис

У Java 16 додали зіставлення зі зразком (pattern matching) для instanceof. Тепер перевіряємо тип і одразу оголошуємо змінну потрібного типу, доступну всередині if-блоку.

Новий синтаксис

if (obj instanceof String s) {
    // s — це вже рядок!
    System.out.println("Довжина рядка: " + s.length());
}
  • Після типу в instanceof пишемо ім’я нової змінної: String s.
  • Змінна доступна лише всередині блоку, де умова істинна.
  • Не потрібно писати приведення типу (String) — компілятор усе гарантує.

У чому магія? Компілятор перевіряє тип і, якщо він підходить, «розкриває» змінну потрібного типу. Інакше блок просто не виконується.

Як це виглядає в реальному коді

Object obj = "Привіт, Java 16+!";

if (obj instanceof String s) {
    System.out.println("Верхній регістр: " + s.toUpperCase());
} else {
    System.out.println("Це не рядок!");
}

3. Безпека та читабельність: чому це круто

Унеможливлюється помилка ClassCastException

Object obj = 123;
// String s = (String) obj; // Бум! Виняток.

За використання зіставлення зі зразком змінна створюється лише за успішної перевірки типу — «неправильного» приведення просто немає.

Код стає коротшим і зрозумілішим
Два рядки перетворюються на один. Менше «шумного» коду — легше читати й підтримувати.

Приклад: обробка різних типів

public static void printInfo(Object obj) {
    if (obj instanceof String s) {
        System.out.println("Рядок довжиною " + s.length());
    } else if (obj instanceof Integer i) {
        System.out.println("Ціле число: " + (i + 1));
    } else {
        System.out.println("Невідомий тип: " + obj);
    }
}

4. Приклади використання та нюанси

Перевірка кількох типів

Object value = ...;

if (value instanceof String s) {
    System.out.println("Це рядок: " + s);
} else if (value instanceof Number n) {
    System.out.println("Це число: " + n);
} else {
    System.out.println("Щось інше: " + value);
}

Використання із null
Якщо об’єкт дорівнює null, то instanceof завжди поверне false — NPE не буде, але блок не виконається.

Object obj = null;

if (obj instanceof String s) {
    // Цей блок НІКОЛИ не виконається, якщо obj == null
    System.out.println("Рядок: " + s);
} else {
    System.out.println("obj — це null або не рядок");
}

Pattern matching з успадкуванням

class Animal {}
class Dog extends Animal {
    void bark() { System.out.println("Гав!"); }
}

Animal a = new Dog();

if (a instanceof Dog d) {
    d.bark(); // Можемо викликати методи Dog без приведення типу!
}

Нагадуємо, порядок перевірок важливий: рухайтеся від більш конкретного до більш загального, інакше «вузькі» перевірки можуть не спрацювати.

Порівняння: старий і новий підхід

Старий спосіб Новий спосіб (pattern matching)
if (obj instanceof Cat) {
    Cat c = (Cat) obj;
    c.meow();
}
if (obj instanceof Cat c) {
    c.meow();
}

5. Обмеження pattern matching для instanceof

  • Область видимості змінної. Змінна із зразка видна тільки в тій гілці, де перевірка істинна.
if (obj instanceof String s) {
    System.out.println(s); // s доступна
}
// System.out.println(s); // Помилка! s тут не видно
  • Не можна оголошувати кілька змінних різних типів в одній умові.
// Помилка компіляції:
if (obj instanceof String s || obj instanceof Integer i) {
    // ...
}
  • Потрібна Java 16+. На старіших версіях JDK новий синтаксис не підтримується.

6. Практичні приклади (на базі простого застосунку)

Уявімо простий Task Manager із різними типами завдань.

Класи

class Task {
    String title;
    public Task(String title) { this.title = title; }
}

class BugTask extends Task {
    int severity;
    public BugTask(String title, int severity) {
        super(title);
        this.severity = severity;
    }
}

class FeatureTask extends Task {
    String feature;
    public FeatureTask(String title, String feature) {
        super(title);
        this.feature = feature;
    }
}

Обробка з pattern matching

public static void processTask(Task t) {
    if (t instanceof BugTask bug) {
        System.out.println("Баг: " + bug.title + ", критичність: " + bug.severity);
    } else if (t instanceof FeatureTask feature) {
        System.out.println("Фіча: " + feature.title + ", модуль: " + feature.feature);
    } else if (t instanceof Task task) {
        System.out.println("Звичайне завдання: " + task.title);
    }
}

7. Корисні нюанси

Таблиця: pattern matching для instanceof — переваги та недоліки

Переваги Недоліки/Обмеження
Менше коду, краща читабельність Потребує Java 16+
Немає ризику ClassCastException Змінна видна лише всередині if-блоку
Безпека типів на рівні компілятора Не працює для кількох типів в одній умові
Зручно для обробки успадкування У старих IDE і збірках може не підтримуватися
Підходить для будь-яких класів

Візуальна схема: як працює pattern matching для instanceof

+---------------------------+
|      Object obj           |
+---------------------------+
            |
            v
  if (obj instanceof Type t)
            |
      Так           Ні
   (true)        (false)
    |               |
    v               v
 t доступна     t не існує
   (код)           (немає доступу)

8. Типові помилки під час використання pattern matching for instanceof

Помилка № 1: Спроба використовувати змінну поза блоком if. Оголосили змінну у зразку, вийшли з блоку — змінної більше немає. Компілятор справедливо лаяється: «Хто така s?»

Помилка № 2: Очікування, що instanceof спрацює для null. Якщо об’єкт дорівнює null, умова instanceof завжди хибна, змінна не створюється. Обробляйте null окремо за потреби.

Помилка № 3: Використання нового синтаксису на старій версії JDK/IDE. На Java 11 і нижче зіставлення зі зразком для instanceof недоступне — отримаєте помилку синтаксису. Перевірте версію JDK.

Помилка № 4: Плутанина з областю видимості. Змінна зі зразка доступна лише в тій гілці, де перевірка істинна. За межами — її не існує.

Помилка № 5: Очікування підтримки кількох типів одночасно. Не можна оголосити відразу дві змінні різних типів в одній умові: if (obj instanceof String s || obj instanceof Integer i) — так не можна. Для кожного типу — своя перевірка.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ