Dziedziczenie klas
Czas przejść w coraz głębsze zagadnienia programowania obiektowego – tym razem tkniemy dziedziczenie.
Choć w Javie można nazywać to inaczej – rozszerzenie.
Na czym polega to całe…
Dziedziczenie – lub inaczej rozszerzanie – zostało stworzone w celu wyodrębniania cech wspólnych obiektów.
Główną zaletą obiektowości jest to, że możemy wyodrębniać cechy wspólne i nie powtarzać ich wielokrotnie – ale to wyjdzie w dalszej części artykułu.
Rozszerzanie – jest to przyjaźniejsza nazwa do zrozumienia dziedziczenia – pozwala na rozszerzanie jednej klasy przez drugą.
Czyli jeżeli jedna klasa dziedziczy po drugiej to znaczy, że ma w sobie wszystko co klasa, po której dziedziczy! Po prostu ma już to w sobie.
Teraz widzisz co oznacza wyodrębnianie cech wspólnych? Tworząc dwie podobne klasy możemy stworzyć trzecią – która będzie zawierała wspólne cechy tych dwóch innych klas – a obie klasy będą po niej dziedziczyć.
Spokojnie – zaraz wszystko się wyjaśni – może na bardziej realnym przykładzie. 😉
Rozszerzanie
Naszym celem będzie stworzenie dwóch klas – Super bohatera oraz Wilkołaka.
Przypuśćmy, że mają one coś wspólnego – są poniekąd ludźmi – mają jakieś imię i wiek.
Dlatego obie klasy bedą rozszerzać klasę Human.
Human
Stwórzmy klasę Human, która zawiera imię i wiek.
public class Human { String name; int age; public Human(String name, int age) { this.name = name; this.age = age; } }
Skoro mamy już klasę bazową – czyli tą, po której będziemy dziedziczyć możemy przejść do następnych klas.
SuperHero
Tak jak wspomniałem Super bohater będzie rozszerzał klasę Human – możemy to zrobić przy użyciu słowa kluczowe extends.
Wygląda to tak:
public class SuperHero extends Human { }
I teraz nasz SuperHero ma wszystko to co Human – i to są właśnie podstawy dziedziczenia. 😉
Dodajmy do naszej klasy pole o super mocy jaką posiada nasz super bohater:
String superPower;
Oraz dodajmy konstruktor, który będzie przyjmował nie tylko superPower – ale również imię i wiek – w końcu musimy jakoś przekazać wartości do klasy Human.
public SuperHero(String name, int age, String superPower) { super(name, age); this.superPower = superPower; }
Wartości do klasy Human przekazujemy przy użyciu super() – wywołuje on konstruktor klasy bazowej – w naszym przypadku wywołaliśmy konstruktor parametrowy – klasy Human oczywiście.
Teraz dzięki temu podczas tworzenia obiektu typu SuperHero mamy w nim zawarte wszystko to co w Human.
Warewolf
Stwórzmy jeszcze klasę odpowiedzialną za reprezentację wilkołaka – będzie ona też rozszerzała człowieka – a dodatkowo będzie przechowywała kolor futra wilkołaka.
Definicja klasy:
public class Warewolf extends Human { }
Pole na kolor futra:
String furColor;
Oraz konstruktor parametrowy – analogicznie stworzony jak w przypadku SuperHero.
Czyli cała nasza klasa wygląda tak:
public class Warewolf extends Human { String furColor; public Warewolf(String name, int age, String furColor) { super(name, age); this.furColor = furColor; } }
Mając już dwie stworzone klasy możemy przejść do testowania.
Testowanie klas
Zacznijmy klasycznie od czystej klasy Main.
public class Main { public static void main(String[] args) { } }
Dodajmy w niej trzy obiekty: Human, SuperHero i Warewolf.
Human human = new Human("Człowiek", 20); SuperHero spiderMan = new SuperHero("SpiderMan", 22, "Strzelanie siecią"); Warewolf firstWarewolf= new Warewolf("First Warewolf", 45, "Czarna");
Wyświetlmy teraz wszystkie imiona naszych postaci:
System.out.println("Human name: " + human.name); System.out.println("SuperHero name: " + spiderMan.name); System.out.println("Warewolf name: " + firstWarewolf.name);
No i zobaczmy co się nam wyświetla:
Human name: Człowiek SuperHero name: SpiderMan Warewolf name: First Warewolf
Zauważ, że każdy obiekt ma w sobie pole name – choć tylko w klasie Human jest zadeklarowane pole name!
Tak jak wspominałem wcześniej – każda klasa rozszerzająca ma odziedziczone wszystkie cechy po klasie bazowej – swoim rodzicu.
Skoro już potrafimy zastosować dziedziczenie – to zróbmy jeszcze jeden myk, na który pozwala dziedziczenie.
Wyodrębnianie
Wspominałem na początku, że rozszerzanie klas pomaga do wyodrębniania cech wspólnych – jednak niesie to ze sobą dodatkową zaletę.
Skoro nasz wilkokał i superbohater jest tak naprawdę człowiekiem – bo rozszerza klasę Human to co, gdyby zrobić coś takiego:
Human superMan = new SuperHero("SuperMan", 30, "Latanie");
To nic – to zadziała.
Dlaczego? Ponieważ SuperHero jest również człowiekiem – nie tylko superbohaterem, dlatego możemy przypisać go do zmiennej typu Human!
Tak samo możemy zrobić z naszym wilkołakiem.
Human secondWarewolf = new Warewolf("Second warewolf", 42, "Srebrna");
I to również będzie śmigać bo Warewolf dziedziczy po Human – czyli sam też jest człowiekiem.
Spróbujmy teraz w drugą stronę:
Warewolf człowiek = new Human("Nowy człowiek", 18);
Ta konstrukcja nie przejdzie – dlaczego?
Zasada jest prosta – klasa dziedzicząca zawiera klasę bazową – lecz nigdy odwrotnie!
Podsumowanie jest proste – człowiek nie jest wilkołakiem, za to wilkołak jest człowiekiem!
Nie można też zrobić takiego myku:
Warewolf człowiek = new SuperHero("Nowy człowiek", 18, "Latanie");
Ponieważ Warewolf i SuperHero nie mają ze sobą nic wspólnego – oboje mają wspólne cechy z czlowiekiem – ale nie ze sobą!
Ze względu, że nasze obie klasy mają coś wspólnego – klasa Human – to możemy je przechowywać w jednej Tablicy typu Human.
Human [] almostHumans = { new Warewolf("First Warewolf", 45, "Czarna"), new SuperHero("SpiderMan", 22, "Strzelanie siecią"), new Warewolf("Second warewolf", 42, "Srebrna"), new Human("Człowiek", 20) };
Podsumowanie
Mam nadzieję, że temat dziedziczenia nie jest już Ci obcy i zrozumiałeś jego podstawy – wszystko się roztrzygnie, gdy uda Ci się rozwiązać poniższe zadania. 😉
- Napisz klasę Car – która będzie zawierała w sobie pola marka samochodu oraz kolor. Stwórz dwie klasy: ElectricCar (zawiera pole, gdzie jest przechowywana pojemność baterii samochodu) oraz FuelCar – gdzie przechowywana jest ilość litrów paliwa. Zwracam uwage, że obie te klasy powinny dziedziczyć po klasie Car. Stwórz w nich metodę getInfo(), która będzie zwracała Stringa wraz z podstawowymi informacjami o danym samochodzie. W main stwórz obiekt elektrycznego i spalinowego samochodu i wyświetl podstawowe jego informację.
- Stwórz klasę Fruit – która będzie zawierała w nazwę owoca oraz metodę getName() zwracającą nazwę owoca. Stwórz klasy odpowiedzialne za reprezentację konkretnego owoca: Apple, Pinneapple, Strawberry – które będą dziedziczyć po klasie Fruit. Stwórz trzy obiekty w main, dodaj je wszystkie do tablicy i wyświetl ich nazwę przy użyciu pętli foreach.
Wystarczy, że rozwiążesz te dwa zadania – dziedziczenie nie jest zbytnio polecane – ale o tym kiedy indziej.
Ważne, żebyś umiał je zastosować oraz wiedział o co chodzi – wykorzystamy je wkrótce podczas pisania aplikacji.
Rozwiązania zadań możesz znaleźć tutaj.