Spis treści
Dziedziczenie w językach obiektowych to tworzenie hierarchii klas. Kolejne klasy w hierarchii posiadają zarówno pola, jak i metody, klas, które je poprzedzają, oraz mogą zawierać nowe pola i metody. Przykładowa hierarchia klas, które reprezentowałyby zwierzęta, mogłaby wyglądać następująco:
Strzałka na rysunku powyżej oznacza, że jedna klasa dziedziczy po drugiej – po stronie grotu strzałki znajduje się klasa nadrzędna, tzn. klasa Kot dziedziczy (jest klasą-dzieckiem) po klasie Ssak, a klasa Ssak dziedziczy po klasie Zwierzę.
Bardzo istotnym aspektem, który można zauważyć patrząc na powyższy rysunek, jest to, że każdy kot to zwierzę, ale nie każde zwierzę, to kot (bo może być nim pies). Podobne zależności są pomiędzy pozostałymi klasami na tym rysunku – każdy ptak to zwierzę, ale nie każde zwierzę, to ptak (bo może nim być ssak) itd.
Pierwszy przykład dziedziczenia¶
Gdy klasa dziedziczy po innej klasie, mówimy, że klasa ta rozszerza tę klasę. Taką klasę będziemy nazywać klasą podrzędną. Klasę rozszerzaną będziemy nazywać klasą bazową bądź klasą nadrzędną. W języku Java do zaznaczenia, że dana klasa ma dziedziczyć po innej klasie, używamy słowa kluczowego extends, po którym następuje nazwa klasy nadrzędnej (klasy-rodzica). Spójrzmy na prosty przykład dziedziczenia:
package prostyprzyklad; public class Osoba { public String imie; public String nazwisko; public String toString() { return "Osoba " + imie + " " + nazwisko; } }
package prostyprzyklad; public class Pracownik extends Osoba { // 1 public int numerIdentyfikatora; public static void main(String[] args) { Osoba pewnaOsoba = new Osoba(); pewnaOsoba.imie = "Jan"; pewnaOsoba.nazwisko = "Kowalski"; System.out.println(pewnaOsoba); Pracownik pewienPracownik = new Pracownik(); pewienPracownik.imie = "Joanna"; // 2 pewienPracownik.nazwisko = "Sikorska"; // 3 pewienPracownik.numerIdentyfikatora = 1234; System.out.println(pewienPracownik); // 4 } }
Osoba to prosta klasa zawierająca pola imie i nazwisko, oraz metodę toString, która zwraca połączone imię i nazwisko osoby.
Klasa Pracownik rozszerza klasę Osoba (1) – zwróć uwagę na słowo kluczowe extends oraz nazwę klasy, która po nim następuje. Oznacza to, że klasa Pracownik, poza polem numerIdentyfikatora, które jest w niej zdefiniowane, posiada także pola imie oraz nazwisko, odziedziczone z klasy Osoba. Dzięki temu, możemy te pola ustawić w obiekcie pewienPracownik (2) (3). To jednak nie koniec – klasa Pracownik dziedziczy także metodę toString – dzięki temu, w linii (4), zamiast wypisać na ekran nazwę klasy i „hash code” obiektu w postaci prostyprzyklad.Pracownik@f6f4d33, zobaczymy imię oraz nazwisko przechowywane w obiekcie pewienPracownik:
Należy zwrócić tutaj uwagę, że klasa Osoba nie wie o istnieniu klasy Pracownik. Co ważniejsze, obiekty klasy Osoba nie posiadają pola numerIdentyfikatora, ponieważ pole to zdefiniowane jest w klasie podrzędnej Pracownik, i tylko obiekty klasy Pracownik mają to pole.
Podobnie, jak w przypadku hierarchii ze zwierzętami z początku tego rozdziału, tutaj także możemy zauważyć, iż każdy Pracownik to Osoba, ale nie każda Osoba to Pracownik, bo możemy utworzyć obiekt klasy Osoba, a obiekt klasy Osoba to obiekt klasy Osoba, a nie Pracownik. Mało tego, moglibyśmy dodać wiele nowych klas dziedziczących po klasie Osoba, i wtedy to rozgraniczenie byłoby jeszcze wyraźniejsze.
Dziedzicząc klasy musimy zwrócić uwagę na kilka reguł i właściwości, o których po kolei sobie opowiemy. Zanim jednak porozmawiamy o szczegółach, zobaczymy przykład wykorzystania polimorfizmu.