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) |
|---|---|
|
|
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) — так не можна. Для кожного типу — своя перевірка.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ