Aplikacje w środowisku Java Materiały do zajęć laboratoryjnych Klasy i obiekty - dziedziczenie mgr inż. Kamil Zieliński Katolicki Uniwersytet Lubelski Jana Pawła II 2018/2019
W ramach poprzedniego laboratorium zaprezentowano tworzenie klasy i metod. Kolejnym, niezwykle ważnym aspektem programowania obiektowego jest pojęcie dziedziczenia. Umożliwia tworzenie hierarchicznej struktury klas w ramach aplikacji. Klasę, która stanowi bazę do dziedziczenia, nazywamy bazową / nadrzędną. Klasy, które dziedziczą, nazywamy pochodnymi / podklasami. Dziedziczenie odbywa się z wykorzystaniem słowa kluczowego Extends. firmy. Utwórzmy klasę bazową Employee, przechowującą podstawowe dane o pracowniku public class Employee { String name; String surname; String job; public Employee(String name,string surname,string job){ this.name = name; this.surname = surname; this.job = job; Należy zwrócić uwagę na konstruktor. Przyjmuje on w parametrach wartości dla wszystkich pól w klasie. Ponieważ wszystkie pola, z których korzystamy, są niestatyczne (oznacza to, że nie można odwołać się do nich bez tworzenia obiektu), możemy zrezygnować z przekazywania obiektu jako parametr. Przygotujmy metodę, pozwalającą na wyświetlenie danych pracownika. public void showinfoaboutemployee(){ System.out.println("Pracownik " + name + " " + surname + " pracuje na stanowisku: " + job ); Taka implementacja metody sprawi, iż wyświetlone zostaną wartości pól obiektu, który ją wywołał. Rozszerzmy klasę Employee dwoma klasami, dodającymi informacje o zarobkach pracowników. public class PhysicalWorker extends Employee { double workhour; double hoursalary; 2
Najważniejsze jest słowo kluczowe extends oraz fakt, że w klasie pojawiają się tylko 2 pola. Dzięki mechanizmowi dziedziczenia, obiekty tej klasy będą miał pola name, surname, job. Napiszmy konstruktor tej klasy, korzystający z dziedziczenia. public PhysicalWorker(String name, String surname, String job, double workhour, double hoursalary){ super(name,surname,job); this.workhour = workhour; this.hoursalary = hoursalary; Nowością jest słowo kluczowe super. Oznacza ono wywołanie konstruktora nadklasy (parametry sugerują, iż jest to konstruktor zaprezentowany na poprzedniej stronie dokumentu). Utwórzmy obiekt i sprawdźmy, jakie będą jego parametry. PhysicalWorker physicalworker = new PhysicalWorker("Andrzej", "Kowalski", "Malarz", 168, 20.5); Zwróćmy uwagę, iż mimo wywołania konstruktora podklasy, utworzony został obiekt posiadający pola z klasy bazowej. Jest to wynik działania słowa kluczowego super. Spróbujmy utworzyć drugą klasę dziedziczącą: public class WhiteCoat extends Employee { double monthlysalary; public WhiteCoat(String name, String surname, String job, double monhtlysalary){ super(name,surname,job); this.monthlysalary = monhtlysalary; Podobnie jak w przypadku klasy PhysicalWorker, napisany został konstruktor, korzystający ze słowa kluczowego super. public WhiteCoat(String name, String surname, String job, double monhtlysalary){ super(name,surname,job); this.monthlysalary = monhtlysalary; 3
Utwórzmy obiekt klasy WhiteCoat: WhiteCoat wc = new WhiteCoat("Adam", "Kowalski", "Kierownik", 6000.00); Mechanizm dziedziczenia umożliwia wykorzystywanie metod z nadklasy. Utwórzmy metodę, pozwalającą na wyświetlenie podstawowych informacji o pracowniku: public void showinfoaboutemployee(){ super.showinfoaboutemployee(); System.out.println("Posiada pensję równą: " + monthlysalary); Klasa bazowa posiada metodę, pozwalającą na wyświetlenie podstawowych informacji o pracowniku. Nie posiadają jej jednak klasy dziedziczące. Dzięki mechanizmowi dziedziczenia, nie ma konieczności pisania całej funkcji od początku. Napiszmy analogiczną funkcję w klasie PhysicalWorker. public void showinfoaboutemployee(){ super.showinfoaboutemployee(); System.out.println("Posiada pensję równą: " + String.format("%.2f",calculateSalary())); Metoda calculatesalary jest metodą w klasie PhysicalWorker i wygląda następująco: private double calculatesalary() { return workhour * hoursalary; Zwróćmy uwagę na strukturę metody showinfoaboutemployee. Znajduje się w niej odwołanie do metody o tej samej nazwie, zadeklarowanej w klasie bazowej. W kolejnej linii dodano rozszerzenie metody z klasy bazowej. Działanie kodu: 4
Dzięki dziedziczeniu nie tylko nie musimy ponownie deklarować pól. Możemy także skorzystać z metod, zaimplementowanych w nadklasie. Obiekty klas dziedziczących, posiadają dostęp do metod z nad-klas. Przygotujmy metodę w klasie Employee i wywołajmy ją przez obiekt klasy dziedziczącej. public void baseclassfunction(){ System.out.println("Metoda z klasy bazowej"); Po utworzeniu obiektu klasy potomnej (PhysicalWorker), możemy skorzystać z metod z klasy bazowej. pw.baseclassfunction(); Udało się wywołać metodę, mimo, iż nie znajduje się ona w klasie PhysicalWorker. Wspomniane wcześniej słowo kluczowe super możemy wykorzystać także w kontekście metod. Napiszmy metodę wyświetlającą tekst i zaimplementujmy ją w klasie Employee. public void showsth(){ System.out.println("Metoda klasy bazowej"); Napiszmy metodę o tej samej nazwie (ale innym ciele) w klasie potomnej. public void showsth(){ System.out.println("Metoda podklasy"); Jeśli wywołamy tę metodę poprzez obiekt podklasy, wyświetlona zostanie: Jeśli usuniemy metodę z klasy PhysicalWorker, ale zostawimy jej wywołanie, wyświetlone zostanie: 5
Jeśli w klasie potomnej wpiszemy nazwę metody (np. w innej metodzie), która znajduje się zarówno w tej klasie jak i w klasie bazowej, wykonana zostanie metoda z tej samej klasy. Istnieje jednak możliwość wywołania metody z nadklasy: public void callingtwomethods(){ showsth(); super.showsth(); pw.callingtwomethods(); Dzięki słowu kluczowemu super, udało się wskazać, która metoda ma być wykonana. 6
Interfejsy Interfejs można określić jako wzorzec, szkielet, zawierający informacje na temat metod, które powinny znaleźć się w klasie. Można stwierdzić, iż interfejs definiuje, co powinna robić klasa, jednak nie określa, w jaki sposób. To należy do klasy implementującej interfejs. W środowisku IntelliJ interfejs tworzymy podobnie jak klasę, jednak z listy wyboru należy wybrać opcję interface. Po dodaniu interfejsu, pojawi się plik: public interface additionaldata { W interfejsie należy wprowadzić metody, które mają posiadać implementujące go klasy. Istotnym jest, że w interfejsie nie określamy ciała metody: public void showhobby(string hobby); Od Javy w wersji 8 istnieje możliwość definiowania domyślnego ciała metody w interfejsie. Realizowane jest to z wykorzystaniem słowa kluczowego default. default public int calc(int a, int b) { return a + b; ; W innym przypadku, ciało metody zostanie dodane w klasie, która zaimplementuje interfejs. Implementacja interfejsu odbywa się z wykorzystaniem słowa implements. public class PhysicalWorker extends Employee implements additionaldata Należy podkreślić, iż klasa może dziedziczyć tylko po jednej klasie (extends ), jednak może również implementować wiele interfejsów. Jak zostało wspomniane, klasa musi zaimplementować WSZYSTKIE metody interfejsu (w przeciwnym wypadku, kompilator zgłosi błąd). 7
public void showhobby(string hobby){ System.out.println("Hobby pracownika to: " + hobby); Każda z klas implementujących interfejs może nadać metodzie całkowicie inne ciało. Również nazwy parametrów mogą się od siebie różnić ważne, by implementacja metody miała taką samą liczbę argumentów o zgodnych typach. Zadania: Utwórz klasę Person, zawierającą informacje o osobie (imię, nazwisko, wiek, nr pesel). Zastosuj odpowiednie typy danych. Zaimplementuj w niej metodę, pozwalającą na wyświetlenie danych. Utwórz konstruktor parametrowy. Utwórz klasę Student, dziedziczącą po klasie Person. Rozszerz ją o nazwę szkoły, klasę i ulubiony przedmiot. Napisz odpowiedni konstruktor oraz metodę wyświetlającą pełne dane o osobie. Wykorzystaj słowo kluczowe super. Utwórz klasę Employee, dziedziczącą po klasie Person. Rozszerz ją o nazwę firmy, zarobki oraz stanowisko. Napisz odpowiedni konstruktor oraz metodę wyświetlającą pełne dane o osobie. Wykorzystaj słowo kluczowe super. Utwórz interfejs AdditionOperation, posiadający metody: showparameters(string parametr) showadditionaldata(string parameter, int parameter2) Zaimplementuj interfejs w klasach dziedziczących. Wykonaj implementację metod interfejsu. Metoda showparameters powinna wyświetlać: Nazwę ulubionego przedmiotu ucznia Imię i nazwisko ulubionego kolegi z pracy Metoda showadditionaldata powinna wyświetlać: Liczbę opuszczonych dni oraz ocenę zachowania ucznia Liczbą wykorzystanych dni urlopu W klasie bazowej napisz metodę wyświetlającą informacje o przedmiocie. Wywołaj ją poprzez obiekt podklasy. 8