Dziedziczenie jest jednym z podstawowych mechanizmów programowania obiektowego. Mechanizm ten umożliwia definiowanie nowych klas na bazie istniejących.
Słowo dziedziczenie kojarzy się nam z życia codziennego z przejmowaniem przez jedną osobę majątku po drugiej osobie. Analogia w języku Java nie jest bardzo daleka. Dziedziczenie oznacza tu przejmowanie przez jedną klasę metod i zmiennych z innej klasy.
Dziedziczenie jest w języku Java mechanizmem wszechobecnym i niezwykle potężnym. Prawie każda klasa – a mówiąc precyzyjniej każda klasa z wyjątkiem klasy java.lang.Object – dziedziczy z jakiejś innej klasy, każda bowiem klasa dziedziczy w sposób niejawny ze wspomnianej klasy Object.
Co dziedziczymy z klasy Object? Kilka metod, a wśród nich metodę equals(…), która służy do sprawdzania równości obiektów. Cóż z tego? Ano chociażby to, że możemy każdy obiekt dowolnego typu porównać z każdym innym każdego dowolnego typu za pomocą dokładnie tej samej metody. Kolejną metodą którą dziedziczymy z klasy Object jest metoda toString(). Metoda ta zwraca tekstową reprezentację obiektu. Ta sama metoda zwraca tekstową reprezentację każdego obiektu, niezależnie od jego typu.
Zobaczmy teraz na przykładzie, w jaki sposób deklarujemy dziedziczenie i w jaki sposób metody zdefiniowane w nadklasie są dziedziczone przez podklasę. Zdefiniujmy wpierw klasę Product, która mogłaby opisywać aktykuły które sprzedajemy w implementowanym przez nas sklepie internetowym. Klasa ta ma jedną metodę, która zwraca cenę towaru i metodę do przypisania tej ceny tuż po utworzeniu obiektu. Kod poniżej:
class Product { private double price; public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
Oczywiście trudno jest sprzedać coś takiego jak produkt. Sprzedajemy raczej konkretne rzeczy, np. książkę, która ma konkretny tytuł, liczbę stron, typ oprawy, autora i numer ISBN. Książka jest produktem i tak jak każdy produkt ma swoją cenę. Skoro książka jest produktem, to ma też wszelkie cechy produktu, zatem klasa która opisuje obiekt książki powinna dziedziczyć z klasy która opisuje produkt, tj. z klasy Product. Przykładowa implementacja klasy modelującej produkty typu książka poniżej:
class Book extends Product { private int pagesNum; public Book(double price, int pagesNum) { this.setPrice(price); this.pagesNum = pagesNum; } public int getPagesNum() { return pagesNum; } }
Zwróćmy uwagę na słówko kluczowe extends w deklaracji klasy. To właśnie w ten sposób oznaczamy, że klasa Book dziedziczy z klasy Product.
Zauważmy, że implementując konstruktor w klasie Book posłużyliśmy się odziedziczoną metodą setPrice(…) aby zapisać cenę książki. Co więcej, klasa Book dziedziczy nie tylko metody zdefiniowane w klasie Product, tj. metody setPrice(…) i getPrice(), ale także metody odziedziczone przez nią z klasy Object. Klasa Product nie dziedziczy jawnie z żadnej klasy, a więc – zgodnie z tym co sobie już powiedzieliśmy – dziedziczy niejawnie z klasy Object. Dziedziczenie działa przechodnio, tak więc metody odziedziczone przez klasę Product przechodzą dalej, do dziedziczącej z niej klasy Book.