CodeGym /Kursy /JAVA 25 SELF /Pattern Matching for instanceof

Pattern Matching for instanceof

JAVA 25 SELF
Poziom 65 , Lekcja 1
Dostępny

1. Problem klasycznego instanceof

Zacznijmy od odrobiny nostalgii. Przed Java 16, gdy trzeba było sprawdzić typ obiektu i następnie użyć go jako tego typu, trzeba było pisać rozwlekły wzorzec ze sprawdzeniem instanceof i jawnym rzutowaniem:

Object obj = ...; // jakiś obiekt

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("Długość łańcucha: " + s.length());
}

Nudny, ale częsty wzorzec

Ten wzorzec pojawia się bardzo często — obsługa zdarzeń, praca z niejednorodnymi kolekcjami, logika biznesowa z dziedziczeniem itd.

Dlaczego to bywa niewygodne

  • Duplikacja kodu — trzeba dwukrotnie wskazać typ: i w instanceof, i w rzutowaniu.
  • Ryzyko błędu — łatwo przypadkowo zrzutować do niewłaściwego typu i otrzymać ClassCastException.
  • „Hałaśliwy” kod — pogarsza czytelność, zwłaszcza przy wielu sprawdzeniach.

2. Pattern Matching for instanceof: nowa składnia

W Java 16 dodano dopasowanie wzorca (pattern matching) dla instanceof. Teraz sprawdzamy typ i od razu deklarujemy zmienną docelowego typu, dostępną wewnątrz gałęzi if.

Nowa składnia

if (obj instanceof String s) {
    // s — to już łańcuch!
    System.out.println("Długość łańcucha: " + s.length());
}
  • Po typie w instanceof podajemy nazwę nowej zmiennej: String s.
  • Zmienna jest dostępna tylko wewnątrz bloku, w którym warunek jest prawdziwy.
  • Nie trzeba pisać rzutowania (String) — kompilator wszystko gwarantuje.

Na czym polega „magia”? Kompilator sprawdza typ i, jeśli pasuje, „odsłania” zmienną docelowego typu. W przeciwnym razie blok po prostu się nie wykona.

Jak wygląda to w realnym kodzie

Object obj = "Cześć, Java 16+!";

if (obj instanceof String s) {
    System.out.println("Wielkie litery: " + s.toUpperCase());
} else {
    System.out.println("To nie jest łańcuch!");
}

3. Bezpieczeństwo i czytelność: dlaczego to jest super

Eliminujemy błąd ClassCastException

Object obj = 123;
// String s = (String) obj; // Boom! Wyjątek.

Z pattern matching zmienna tworzy się tylko przy pomyślnym sprawdzeniu typu — „nieprawidłowego” rzutowania po prostu nie ma.

Kod staje się krótszy i bardziej zrozumiały
Dwa wiersze zamieniają się w jeden. Mniej „szumu” — łatwiej czytać i utrzymywać.

Przykład: obsługa różnych typów

public static void printInfo(Object obj) {
    if (obj instanceof String s) {
        System.out.println("Napis o długości " + s.length());
    } else if (obj instanceof Integer i) {
        System.out.println("Liczba całkowita: " + (i + 1));
    } else {
        System.out.println("Nieznany typ: " + obj);
    }
}

4. Przykłady użycia i niuanse

Sprawdzanie kilku typów

Object value = ...;

if (value instanceof String s) {
    System.out.println("To jest łańcuch: " + s);
} else if (value instanceof Number n) {
    System.out.println("To jest liczba: " + n);
} else {
    System.out.println("Coś innego: " + value);
}

Użycie z null
Jeśli obiekt jest równy null, to instanceof zawsze zwróci false — NPE nie będzie, ale blok się nie wykona.

Object obj = null;

if (obj instanceof String s) {
    // Ten blok NIGDY się nie wykona, jeśli obj == null
    System.out.println("Łańcuch: " + s);
} else {
    System.out.println("obj — to null albo nie łańcuch");
}

Pattern matching z dziedziczeniem

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

Animal a = new Dog();

if (a instanceof Dog d) {
    d.bark(); // Możemy wywoływać metody Dog bez rzutowania!
}

Przypominamy: kolejność sprawdzeń ma znaczenie — idź od bardziej konkretnego do bardziej ogólnego, inaczej „wąskie” sprawdzenia mogą nie zadziałać.

Porównanie: stare i nowe podejście

Stary sposób Nowy sposób (pattern matching)
if (obj instanceof Cat) {
    Cat c = (Cat) obj;
    c.meow();
}
if (obj instanceof Cat c) {
    c.meow();
}

5. Ograniczenia pattern matching dla instanceof

  • Zasięg zmiennej. Zmienna z wzorca jest widoczna tylko w tej gałęzi, w której sprawdzenie jest prawdziwe.
if (obj instanceof String s) {
    System.out.println(s); // s dostępna
}
// System.out.println(s); // Błąd! s nie jest tu widoczna
  • Nie można deklarować kilku zmiennych różnych typów w jednym warunku.
// Błąd kompilacji:
if (obj instanceof String s || obj instanceof Integer i) {
    // ...
}
  • Wymagana jest Java 16+. Na starszych wersjach JDK nowa składnia nie jest obsługiwana.

6. Praktyczne przykłady (na bazie prostej aplikacji)

Wyobraźmy sobie prosty Task Manager z różnymi typami zadań.

Klasy

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

Przetwarzanie z pattern matching

public static void processTask(Task t) {
    if (t instanceof BugTask bug) {
        System.out.println("Bug: " + bug.title + ", krytyczność: " + bug.severity);
    } else if (t instanceof FeatureTask feature) {
        System.out.println("Funkcja: " + feature.title + ", moduł: " + feature.feature);
    } else if (t instanceof Task task) {
        System.out.println("Zwykłe zadanie: " + task.title);
    }
}

7. Przydatne niuanse

Tabela: pattern matching dla instanceof — plusy i minusy

Zalety Wady/Ograniczenia
Mniej kodu, lepsza czytelność Wymaga Java 16+
Brak ryzyka ClassCastException Zmienna widoczna tylko wewnątrz bloku if
Bezpieczeństwo typów na poziomie kompilatora Nie działa dla wielu typów w jednym warunku
Wygodne do obsługi dziedziczenia Może nie być obsługiwane w starszych IDE i narzędziach budujących
Nadaje się do dowolnych klas

Schemat: jak działa pattern matching dla instanceof

+---------------------------+
|      Object obj           |
+---------------------------+
            |
            v
  if (obj instanceof Type t)
            |
      Tak           Nie
   (true)        (false)
    |               |
    v               v
 t dostępna     t nie istnieje
   (kod)         (brak dostępu)

8. Typowe błędy przy użyciu pattern matching for instanceof

Błąd nr 1: Próba użycia zmiennej poza blokiem if. Zadeklarowałeś zmienną we wzorcu, wyszedłeś z bloku — zmiennej już nie ma. Kompilator słusznie protestuje: „Kim jest s?”

Błąd nr 2: Oczekiwanie, że instanceof zadziała dla null. Jeśli obiekt jest równy null, warunek instanceof jest zawsze fałszywy, zmienna nie jest tworzona. Obsługuj null osobno, jeśli trzeba.

Błąd nr 3: Używanie nowej składni na starej wersji JDK/IDE. W Java 11 i starszych dopasowanie wzorca dla instanceof jest niedostępne — otrzymasz błąd składni. Sprawdź wersję JDK.

Błąd nr 4: Zamieszanie z zasięgiem. Zmienna z wzorca jest dostępna tylko w tej gałęzi, w której sprawdzenie jest prawdziwe. Poza nią — nie istnieje.

Błąd nr 5: Oczekiwanie wsparcia wielu typów jednocześnie. Nie można zadeklarować od razu dwóch zmiennych różnych typów w jednym warunku: if (obj instanceof String s || obj instanceof Integer i) — tak nie wolno. Dla każdego typu — osobne sprawdzenie.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION