– Cześć, Amigo! Dziś zajmiemy się tematem, który bardzo Ci się przyda. Mam na myśli dziedziczenie.

Dla niewtajemniczonych programowanie jest jak magia. Pozwól, że zacznę od analogii...

Załóżmy, że jesteś czarodziejem, który chciałby stworzyć latającego konia. Mógłbyś spróbować wyczarować Pegaza. Ale latające konie nie pojawiają się ot tak, więc masz pecha. Przed Tobą dużo pracy. Może byłoby łatwiej zacząć od zwykłego konia, a potem dopiero zająć się skrzydłami.

Dziedziczenie. Zalety dziedziczenia - 1

W programowaniu nazywamy ten proces «dziedziczeniem». Załóżmy, że musisz napisać bardzo złożoną klasę. Dużo czasu zajmie Ci pisanie kodu od zera i sprawdzanie go pod kątem ewentualnych błędów. Tylko po co sobie utrudniać? Rozejrzyj się wokół – być może klasa, której szukasz, już istnieje?

Powiedzmy, że znalazłeś klasę, która zapewnia Ci 80% funkcjonalności, której potrzebujesz. Możesz po prostu skopiować jej kod do Twojej klasy. Ma to niestety swoje wady:

1) Znaleziona klasa mogła zostać przedtem skompilowana do kodu bajtowego. Możesz zatem nie mieć dostępu do jej kodu źródłowego.

2) Być może posiadasz kod źródłowy tej klasy, ale firma, dla której pracujesz, może w efekcie zostać pozwana na kilka miliardów za wykorzystanie 6 linijek czyjegoś kodu. Mogą pozwać także Ciebie.

3) Prowadzi to do niepotrzebnego duplikowania dużej ilości kodu. I jeśli autor tej innej klasy znajdzie i naprawi w nim błąd, Twój błąd nie znika.

Istnieje na szczęście inne rozwiązanie, bardziej eleganckie, które nie wymaga uzyskania pozwolenia na wykorzystanie kodu oryginalnej klasy. W Javie możesz zwyczajnie zadeklarować tę inną klasę jako klasę macierzystą Twojej klasy. To jest równoważne z dodaniem kodu tej klasy do Twojej własnej klasy. Wszystkie dane i metody z klasy macierzystej pojawią się także w Twojej klasie. Na przykład, możesz dziedziczyć z «horse», dodać «wings» i otrzymać «Pegasus».

Dziedziczenie. Zalety dziedziczenia - 2

– Bardzo ciekawe. Proszę, kontynuuj.

– Dziedziczenie ma także inne zastosowania. Załóżmy, że masz dziesięć bardzo podobnych do siebie klas. Mają pasujące dane i metody. Możesz utworzyć specjalną klasę bazową, przenieść te dane (i powiązane z nimi metody) do klasy bazowej i sprawić, by te dziesięć klas z niej dziedziczyło. Innymi słowy, dla każdej klasy wskazujesz, że ma ona klasę macierzystą, zwaną również klasą bazową.

– Tak jak abstrakcja wykazuje najwięcej zalet w połączeniu z enkapsulacją, tak zalety dziedziczenia wzmacniane są przez polimorfizm. Ale o tym opowiem Ci jutro. Dzisiaj przyjrzymy się kilku przykładom dziedziczenia.

– Załóżmy, że piszemy program do gry w szachy. Musimy stworzyć klasy dla figur szachowych. Amigo, jakie klasy proponujesz?

– King, Queen, Bishop, Knight, Rook i Pawn.

– Bardzo dobrze. Niczego nie ominąłeś.

– A jak myślisz, jakie dane będziemy przechowywać w tych klasach?

– Współrzędne pól wszystkich figur (x i y) i ich wartości. Niektóre figury są przecież bardziej wartościowe niż inne.

– Jaka jest różnica między tymi klasami?

– Różnią się tym, jak przemieszczają figury. Swoim zachowaniem.

– Tak. Można zdefiniować je jako klasy w następujący sposób:

class King
{
int x;
int y;
int worth;
void kingMove()
{
//kod, który definiuje,
//jak przemieszcza się król
}
}
class Queen
{
int x;
int y;
int worth;
void queenMove()
{
//kod, który definiuje,
//jak przemieszcza się królowa
}
}
class Rook
{
int x;
int y;
int worth;
void rookMove()
{
//kod, który definiuje,
//jak przemieszcza się wieża
}
}
class Knight
{
int x;
int y;
int worth;
void knightMove()
{
//kod, który definiuje,
//jak przemieszcza się skoczek
}
}
class Bishop
{
int x;
int y;
int worth;
void bishopMove()
{
//kod, który definiuje,
//jak przemieszcza się goniec
}
}
class Pawn
{
int x;
int y;
int worth;
void pawnMove()
{
//kod, który definiuje,
//jak przemieszcza się pionek
}
}

– Tak, dokładnie tak sam bym to napisał.

– Przyjrzyj się temu, w jaki sposób przy użyciu dziedziczenia możesz zmniejszyć kod. Możemy przenieść identyczne metody i dane do wspólnej klasy. Nazwijmy ją ChessItem. Nie ma sensu tworzyć obiektów ChessItem, skoro nie odpowiadają one żadnej figurze szachowej. Ale sama klasa byłaby niezwykle pomocna:

class King extends ChessItem
{
void kingMove()
{
//kod, który definiuje,
//jak przemieszcza się król
}
}
class Queen extends ChessItem
{
void queenMove()
{
//kod, który definiuje,
//jak przemieszcza się królowa
}
}
class Rook extends ChessItem
{
void rookMove()
{
//kod, który definiuje,
//jak przemieszcza się wieża
}
}
 class ChessItem
{
int x;
int y;
int worth;
}
 
class Knight extends ChessItem
{
void knightMove()
{
//kod, który definiuje,
//jak przemieszcza się skoczek
}
}
class Bishop extends ChessItem
{
void bishopMove()
{
//kod, który definiuje,
//jak przemieszcza się goniec
}
}
class Pawn extends ChessItem
{
void pawnMove()
{
//kod, który definiuje,
//jak przemieszcza się pionek
}
}

– Ależ ciekawe!

– Dokładnie. Korzyści z tego są nieocenione, zwłaszcza przy projektach zawierających tysiące różnych obiektów i setki klas. W tym przypadku odpowiednio dobrane klasy mogą nie tylko znacząco uprościć logikę, ale także skrócić aż dziesięciokrotnie wymagany kod.

– To co trzeba zrobić, żeby dziedziczyć po klasie?

– Gdy już zadeklarujemy klasę, używamy słowa „extends”, a po nim piszemy nazwę klasy macierzystej. Możesz dziedziczyć tylko po jednej klasie.

Dziedziczenie. Zalety dziedziczenia - 3

Na obrazku widzimy "cow", która dziedziczy po «pig». «Pig» dziedziczy po «chicken», a «chicken» dziedziczy po «egg». Każda klasa ma tylko jedną klasę macierzystą! Takie dziedziczenie nie zawsze jest logiczne. Jeśli masz tylko świnię, ale chcesz mieć krowę, programiści często nie mogą oprzeć się pokusie, aby z «cow» zrobić «pig».

– A co, jeśli chcę dziedziczyć po dwóch klasach? Czy to w ogóle możliwe?!

– Niestety nie. Klasy w Javie nie obsługują wielokrotnego dziedziczenia implementacji: klasa może mieć tylko jedną klasę macierzystą. Ale istnieje coś takiego jak wielokrotne dziedziczenie typu, które oznacza, że klasa może zaimplementować więcej niż jeden interfejs. To w jakimś stopniu rozwiązuje ten problem.

– Rozumiem. A co to takiego, ten interfejs?

– O interfejsach opowiem Ci jutro. Teraz przyjrzyjmy się bliżej naszemu dziedziczeniu.