Sierpień 8, 2018

Dziedziczenie klas

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ł 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 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. 😉

  1. 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ę.
  2. 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.