![]()
W poprzednich lekcjach poznaliśmy klasy i obiekty – nauczyliśmy się tworzyć własne typy danych, wywoływać metody, a także konwertować typy danych na inne. Teraz czas na kolejny krok w programowaniu obiektowym: dziedziczenie i polimorfizm. Są to jedne z podstawowych mechanizmów programowania obiektowego w Javie. Dzięki nim Twój kod staje się bardziej elastyczny i łatwiejszy w utrzymaniu.
Wprowadzenie
W tej lekcji zobaczysz, jak tworzyć klasy dziedziczące po innych klasach oraz jak wykorzystać polimorfizm w praktyce. Dowiesz się, czym jest dziedziczenie w Javie, czyli jak jedna klasa może przejąć właściwości i metody innej klasy i jak można je wykorzystać do tworzenia klas powiązanych relacją. Omówimy również użycie słowa kluczowego super oraz zobaczysz czym jest polimorfizm, czyli jak obiekty różnych klas mogą być traktowane w podobny sposób, przy jednoczesnym zachowaniu swoich zachowań.
Najpierw omówimy, jak tworzyć klasy bazowe i dziedziczące oraz jak korzystać z metody super. Następnie pokażemy przykłady polimorfizmu, zarówno w formie metod nadpisanych (overriding), jak i w wykorzystaniu referencji typu bazowego do obiektów typu pochodnego. Na końcu artykułu poznasz praktyczne zastosowanie tych koncepcji w testowaniu i programowaniu.
Dziedziczenie
Dziedziczenie pozwala tworzyć nową klasę na podstawie istniejącej. Klasa dziedzicząca (podklasa) automatycznie otrzymuje pola i metody klasy nadrzędnej – bazowej (superklasy), co ułatwia ponowne użycie kodu. Dzięki temu unikamy powtarzania kodu i możemy w prosty sposób rozszerzać funkcjonalność programu.
Przykład:

W tym przykładzie klasa Samochod dziedziczy po klasie Pojazd. Dzięki temu Samochod automatycznie ma pole marka i metodę uruchom().
Relacja super
Słowo kluczowe super pozwala podklasie odwołać się do pól lub metod klasy nadrzędnej. Jest bardzo przydatne, gdy chcemy rozszerzyć zachowanie metody z klasy bazowej, zamiast ją całkowicie nadpisywać.
Przykład:

super.uruchom() wywołuje metodę klasy bazowej: Pojazd, a następnie wykonuje się dodatkowy kod w podklasie: Samochod.uruchom(). Dzięki temu rozszerzamy zachowanie klasy bazowej zamiast je zastępować.
Polimorfizm
Polimorfizm pozwala obiektom przyjmować różne formy. Ta sama metoda może zachowywać się różnie w zależności od obiektu, który ją wywołuje.
Wyobraź sobie aplikację, która obsługuje różne rodzaje użytkowników: administratorów, klientów, gości. Każdy z nich loguje się w inny sposób, ale z punktu widzenia kodu wystarczy jedna metoda np. zaloguj().
W Java wykorzystujemy dwa rodzaje polimorfizmu: statyczny i dynamiczny.
Polimorfizm statyczny (overloading)
Występuje wtedy, gdy w jednej klasie mamy kilka metod o tej samej nazwie, ale z różnymi parametrami tzw. przeciążanie metod (overloading). Zanim program się uruchomi Java decyduje, którą wersję metody wywołać już na etapie kompilacji.
Przykład:

W powyższym przykładzie pokazano przeciążanie metod w klasie Serwis. Kompilator sam decyduje, którą wersję metody uruchom() wywołać – na podstawie liczby oraz typów przekazanych argumentów.
Całość odbywa się w jednej klasie, bez dziedziczenia ani nadpisywania metod (czyli bez mechanizmu overriding).
Takie podejście pozwala tworzyć kilka metod o tej samej nazwie, które różnią się tylko parametrami.
Polimorfizm dynamiczny (overriding)
Występuje, gdy podklasa nadpisuje metodę klasy nadrzędnej. Java decyduje, którą wersję metody wywołać dopiero w czasie działania programu.
Przykład:

Choć zmienne p1 i p2 mają typ Pojazd, w czasie działania program wybiera metodę jedz() odpowiednią dla faktycznego obiektu (Samochod lub Motocykl). To przykład polimorfizmu dynamicznego – metoda jest wywoływana w zależności od rzeczywistego typu obiektu w pamięci.
@Override to adnotacja w Javie, która informuje, że dana metoda nadpisuje metodę z klasy bazowej lub interfejsu. Kompilator sprawdza, czy metoda faktycznie istnieje w klasie nadrzędnej i w przypadku błędu (np. literówki, zmiany listy parametrów lub niezgodności typów) zgłasza problem. Bez tej adnotacji kompilator nie ostrzegłby Cię, że coś przestało działać zgodnie z intencją.
Adnotacja nie jest wymagana, program skompiluje się i uruchomi poprawnie bez niej. Jednak warto jej używać zawsze przy nadpisywaniu metod, ponieważ zwiększa bezpieczeństwo kodu, poprawia czytelność i ułatwia wychwycenie błędów.
Podsumowanie
W tej lekcji poznałeś dwa kluczowe mechanizmy programowania obiektowego w Javie: dziedziczenie i polimorfizm.
Dziedziczenie pozwala tworzyć nowe klasy na podstawie istniejących, dzięki czemu możemy ponownie wykorzystywać kod i łatwo rozszerzać funkcjonalność. Zobaczyliśmy też, jak używać super, które umożliwia wywoływanie metod klasy bazowej i dodanie własnego zachowania w podklasie.
Polimorfizm umożliwia traktowanie obiektów różnych klas w podobny sposób, przy jednoczesnym zachowaniu ich indywidualnych zachowań. Rozróżniliśmy dwie formy:
- statyczny (przeciążanie metod, overloading) – decyzja kompilatora, metoda wybierania w czasie kompilacji,
- dynamiczny (nadpisywanie metod, overriding) – decyzja w czasie działania programu, dzięki której wywoływana jest metoda odpowiadająca rzeczywistemu typowi obiektu.
Dzięki dziedziczeniu i polimorfizmowi kod staje się bardziej elastyczny, czytelny i łatwiejszy w utrzymaniu.
W kolejnej części kursu możesz nauczyć się, jak tworzyć abstrakcyjne klasy i interfejsy, które pozwolą jeszcze lepiej wykorzystywać polimorfizm w praktyce.
O autorze
Tester oprogramowania z pasją do jakości i technologii. Łączy doświadczenie w testach manualnych i automatycznych, koncentrując się przede wszystkim na aplikacjach webowych oraz usługach SOAP i REST. Testowanie to dla niego nie tylko szukanie błędów, ale przede wszystkim kwestionowanie przyjętych założeń i usprawnianie tego, co nie działa – również w samym procesie testowym. Lubi zmieniać, angażować się i aktywnie budować proces testowy, tak by miał sens, a nie tylko formę. Prywatnie fan rocka, biwakowania, podróży.
