Szkolenia programowania we Wrocławiu
Kurs Java Podstawy - rozszerzony

Dziedziczenie cd. i konstrukcja super()

Kontynuując tematykę dziedziczenia przyjrzyjmy się dokładniej jak wykorzystywać już raz zdefiniowane metody oraz konstruktory. W końcu czasami mimo iż rozszerzamy inną klasę, to chcielibyśmy mieć możliwość dodania do nich czegoś dodatkowego.

Zacznijmy od przykładu. Niech klasą bazową będzie Pracownik, a rozszerzać ją będą Lekarz oraz Pielęgniarka. Pielęgniarka oprócz podstawowych pól odziedziczonych z Pracownika, czyli imię, nazwisko i wypłata, posiada również informację o ilości nadgodzin, natomiast Lekarz o premii.

public class Pracownik {
	private String imie;
	private String nazwisko;
	private double wyplata;

	public Pracownik(String imie, String nazwisko, double wyplata){
		this.imie = imie;
		this.nazwisko = nazwisko;
		this.wyplata = wyplata;
	}

	String getImie(){ return imie; }
	String getNazwisko(){ return nazwisko; }
	double getWyplata(){ return wyplata; }
}

public class Pielegniarka extends Pracownik{
	private int nadgodziny;

	public Pielegniarka(String imie, String nazwisko, double wyplata) {
		super(imie, nazwisko, wyplata);
		nadgodziny = 0;
	}

	public int getNadgodziny(){ return nadgodziny; }
	public void setNadgodziny(int n){
		nadgodziny += n;
	}
}

public class Lekarz extends Pracownik{
	private double premia;

	public Lekarz(String imie, String nazwisko, double wyplata) {
		super(imie, nazwisko, wyplata);
		premia = 0;
	}

	public double getPremia(){ return premia; }
	public void setPremia(double d){
		premia = d;
	}
}

Widzimy tu nieznaną z poprzedniej lekcji o dziedziczeniu konstrukcję z wykorzystaniem słowa super. Służy ona do wywoływania konstruktorów klasy nadrzędnej (tej, z której dziedziczymy) o podanych parametrach, lub w przypadku metod - odpowiedników o takiej samej nazwie i podanych parametrach.

Zdefiniowaliśmy konstruktor przyjmujący 3 argumenty, ponieważ chcemy mieć przynajmniej podstawowe dane dotyczące pracownika, czyli imię, nazwisko i wypłatę. Klasa Pielęgniarka rozszerza klasę pracownik o dodatkowe pole nadgodziny, które w konstruktorze jest ustawiane na wartość 0. Bez sensu byłoby przepisywanie w konstruktorze tego samego kodu, którego użyliśmy w klasie pracownik  - w końcu także po to użyliśmy dziedziczenia. Na szczęście poprzez konstrukcję

super(<em>parametry</em>)

możemy wywołać konstruktor o takich samych parametrach jak odpowiednik z  klasy, z której dziedziczyliśmy. Analogicznie postąpiliśmy w przypadku klasy Lekarz.

Od teraz, gdy będziemy tworzyli przykładowo nowy obiekt klasy Pielegniarka:

Pielegniarka pielegniarka = new Pielegniarka("Asia", "Kowalska", 2000);

Kolejno wykona się konstruktor klasy Pracownik (a właściwie jego kod w klasie Pielegniarka), a następnie zainicjowane zostanie pole nadgodziny wartością 0. Czyli tak naprawdę oba poniższe kody są sobie równe:

	public Pielegniarka(String imie, String nazwisko, double wyplata) {
		super(imie, nazwisko, wyplata);
		nadgodziny = 0;
	}

	public Pielegniarka(String imie, String nazwisko, double wyplata){
		this.imie = imie;
		this.nazwisko = nazwisko;
		this.wyplata = wyplata;
		nadgodziny = 0;
	}

Podobnie możemy postępować z metodami o takich samych nazwach i takich samych argumentach. Należy jednak uważać na mały szczegół. Samo wywołanie metody super() powoduje wywołanie konstruktora klasy nadrzędnej. Jeśli chcemy wywołać metodę nadklasy należy użyć operatora kropki, przykładowo:

public void metoda(){
  super.metoda(<em>ewentualne parametry</em>);
   ...
}

Dodatkowo instrukcja super() musi być pierwszą instrukcją w konstruktorze. W innym wypadku otrzymamy błąd Constructor call must be the first statement in a constructor

<- Poprzednia LekcjaNastępna Lekcja ->

Komentarze

Bartłomiej

Witaj!

Pielegniarka pielegniarka = new Pielegniarka("Asia", "Kowalska", "2000");

tutaj jest chyba błąd - wypłata nie jest stringiem tylko double...

Slawek

Masz rację, dzięki :)

Paweł

public Lekarz(String imie, String nazwisko, double wyplata, double premia) {
super(imie, nazwisko, wyplata);
premia = 0;
}

A czy mogę umieścić parametr double premia jak wyżej?
I dlaczego np. zmienna int nadgodziny jest prywatna a metoda zwracajaca premie jest juz publiczna? Analogicznie z premią.

Slawek

Ale gdzie chcesz umieścić premię? Jeżeli w super() to nie możesz tego zrobić, ponieważ: super() wywołuje jakiś konstruktor "super" klasy, czyli tej klasy po której dziedziczymy. U nas klasa Lekarz dziedziczy po klasie Pracownik, a klasa Pracownik nie posiada pola premia, ba nawet nie ma pojęcia, że istnieje jakaś klasa Lekarz, która ma takie pole.

Co do tego, czemu pola są prywatne. Jest to założenie enkapsulacji, czy jak ktoś woli hermetyzacji danych. Generalnie staramy się ograniczać bezpośredni dostęp do wszelkich memberów klasy i umożliwiamy do nich dostęp przez gettery i settery. Czyniąc jakieś pole prywatnym i nie udostępniając settera (metody ustawiającej wartość), możemy je uczynić jako tylko do odczytu (przez getter). Podejście takie przyjęło się w programowaniu obiektowym i pozwala na przykład w wygodny sposób kontrolować to jakie dane może wprowadzać użytkownik. Prosty przykład walidacji. Powiedzmy, że lekarz może dostawać premię, ale nie większą niż 2000zł. Gdy mamy setter(zwany też czasami mutatorem) wystarczy dodać jeden warunek if, natomiast, gdy damy do pola premia dostęp publiczny, cała odpowiedzialność za walidację danych spada na programistę korzystającego z naszego kodu. Settery pozwalają więc pisać kod odporny na błędy.
Jest to swego rodzaju standard i powiedziałbym nawet, że jeśli wiemy, że coś nie musi być publiczne(a powinny być takie w sumie tylko stałe), to należy ograniczyć dostęp do co najmniej pakietowego (protected). Warto zauważyć, że praktycznie wszystkie klasy standardowej biblioteki Javy posiadają teraz metody dostępowe do pól obiektów, nawet jeśli nie posiadały ich kiedyś to wraz z aktualizacjami je dodano. Więcej informacji na ten temat wraz z przykładami można znaleźć w sieci.

Mereel

Nie moge wyswietlic pol premia i nadgodziny 'do odczytu'. co robic?
----------------------------------------------------------
Lekarz lekarz = new Lekarz("Roman","Niedzielski",2000);
double premia = 1000;
System.out.println("Imię: "+lekarz.getImie() );
System.out.println("Nazwisko: "+lekarz.getNazwisko() );
System.out.println("Wypłata: "+lekarz.getWyplata () );
System.out.println("Premia: "+ lekarz.getPremia()+"\n" );

Slawek

Jeśli masz kod z tej lekcji to:
Lekarz dziedziczy po klasie Pracownik. Lekarz nie ma więc pojęcia, że istnieje w ogóle klasa Pielęgniarka, gdzie jest zdefiniowane pole nadgodziny.
Premia powinna działać normalnie, w tym fragmencie, który wkleiłeś masz spację przed nawiasami lekarz.getWyplata (), co jest błędem składniowym.

G.

Myśle ze warto byłoby zaznaczyć konwencję programów obiektowo orientowanych - enkapsulacje którą zaczłeś teraz używać - w Javie jedyne dane które mogą być zmieniane to te którym pozwalamy przez set... a tak to wszystko ma private.
Bo to nagłe private przy polach może trochę zdziwić. :)

Fajny kurs;)

Slawek

w sumie to dwa komentarze nad Twoim właśnie o tym pisałem :)
Z ukrywaniem pól też nie można popadać w zbytnią paranoję, tam gdzie się da bezpiecznie można wykorzystać dostęp pakietowy. Podejście mutatorów i getterów jest natomiast niezbędne przy programowaniu aplikacji w J2EE, gdzie klasy typu Osoba - które przechowują jakieś tam dane będą nazywane Beanami.

G.

A no tak :D
moje nie dopatrzenie :)

GJeszka

Fajny kurs, a gdzie się podziało zadanie do wykonania ;]

Witek

Witam
Pewnie niedługo będzie.

Kelzaer

Do tej pory nic nie ma :(

Witek

Witam
Może wymyślimy jakieś proste zadanko na dziedziczenie tak jak w przykładzie z tej lekcji.

Bartek

Czy ktoś może mi wytłumaczyć kiedy, po co używamy i co daje konstrukcja super(); ?

Przemek

zadania by się przydały :(

kankug

po co jest get przed Nazwisko itd.? Może coś zapomniałem bo byłem na "urlopie" od Javy przez pół miesiąca więc proszę nie obrażać!

Witek

Witam
Masz „ get ” ponieważ wszystkie trzy zmienne masz zadeklarowane jako prywatne.
Pozdrawiam.

milosznick

@Witek "Masz „ get ” ponieważ wszystkie trzy zmienne masz zadeklarowane jako prywatne."
Nie jestem ekspertem (jeszcze:p) ale śmiem twierdzić że Witek wprowadza w błąd. Jeszcze nie wiem co to są gettery dokładnie ale wiem że "get" nie ma żadnej cudownej funkcjonalności odczytu pól prywatnych ponieważ... nie musi wcale mieć.
Metody (np. te w klasie Pracownik) zawierające "get" w nazwie mają dostęp "z urzędu" do prywatnych pól bo... są definiowane w tej samej klasie Pracownik.
Proponuję test: zmienić "get" na "daj".
Sławek tu trochę zamieszał, sam dużo musiałem główkować jak to "get" działa i jeszcze te gettery w komentarzach. Proponował bym to na tym etapie zmienić na "daj".
Proszę mnie poprawić jeśli źle to widzę.
Pozdrawiam.

Sławek Ludwiczak

Tak jak piszesz w ramach klasy Pracownik dostęp do tych pól jest nieograniczony, jednak poza tą klasą dostęp do tych pól bez gettera i settera jest niemożliwy. Można więc się zastanawiać jaki jest sens wprowadzania takiego rozwiązania, a nie dać po prostu tych pól jako publicznych?
Ano taki, że taka jest konwencja i na wyższych etapach programowania jest to po prostu wymóg (szczególnie w JEE przez tzw JavaBeans). Zmiana nazwy na dajPole() wprowadzi więc w tym miejscu jeszcze więcej złego.
Odpowiadając jednak jeszcze dalej po co takie metody tworzyć? Przede wszystkim dla bezpieczeństwa - mając dostęp publiczny do pola możemy przypisać co nam się tylko podoba i zrzucamy potrzebę walidacji na użytkownika takiej klasy (co byłoby szczególnie uciążliwe w przypadku korzystania z czyichś klas). W przypadku używania mechanizmów getter/setter to na twórcy klasy spoczywa odpowiedzialność za jej poprawne działanie - Ty już nie musisz się przejmować tym, że np będziesz próbował przypisać jakąś wartość w nieprawidłowym formacie.
Druga praktyczna rzeczy to fakt, że wpisując w IDE nazwaPola.get i wciskając ctrl+spacja otrzy6mujesz automatycznie całą listę pól, do których masz dostęp - nie musisz się martwić o to jak je ktoś sobie ponazywał i nie musisz się ich doszukiwać.

milosznick

Witam Cię Sławku i dziękuje za odpowiedz.
Dużo mi twój post rozjaśnił (idea get/set).
Wracając do samego początku... kankug napisał "po co jest get przed Nazwisko itd.?"
Chodziło mi dokładnie o to że, słowo "get" gettera jeszcze nie czyni, bo czy w klasie testującej wywołam "lekarz.getImie()" czy "lekarz.dajImie()" (oczywiście po zmianie nazwy metody w klasie Pracownik)
to mam dostęp do wszystkich pól prywatnych i program działa tak samo.
Rozumiem że, to jest konwencja nazewnictwa by nazwę metody typu getter/setter zaczynać od get/set a wpływu na działanie programu nie ma.
Pozdrawiam.

Kamil

Nie rozumiem, jak wykasujemy linijkę z super z Lekarza i Pielegniarki, program i tak sie kompiluje i robi to samo, czyli automatycznie wrzuca domyślne dane z Pracownika

Pendu

Fell out of bed feeling down. This has breentihgd my day!

czytacz

W tekscie jest powazny blad utrudniajacy poprawne rozumienie .
Glowilem sie czytajac "nad klasy" , nad jakie klasy ? do licha , o co czlowiekowi chodzi ?
W koncu pojalem , ze chodzi o rzeczownik "nadkasa" , czyli klasa wyzsza , wiec piszemy razem "nadklasy" , tak zeby sobie nie utrudniac .

Asr

niema zadań bo to jest na tyle banalne że niema sensu ;p to jak w c++ metody dostępowe żęby się dostać do prywatnego pola

Sławek Ludwiczak

metody dostępowe, czyli tzw gettery to rzecz zupełnie poboczna dla tego co jest tutaj opisywane :) W tej lekcji chodzi o wykorzystanie konstruktora, czy metody klasy nadrzędnej.

Deli

Lekarz dostaje premie za nadgodziny pielęgniarki - jakie to polskie i życiowe zarazem :D

Kreet

Dziwi mnie, że nie został ładnie objaśniony ten nagły specyfikator dostępu private. Jak ktoś nie ma pojęcia co to jest to może trochę rozproszyć.

SAK

Jak sprawdziłem klasie Pelegniarka można przekazywać pole "nadgodziny" bezpośrednio w parametrach, tzn:

public Pielegniarka (String imie, String nazwisko, double wyplata, int nadgodz){
super (imie, nazwisko, zaplata);
this.nadgodz=nadgodz;
}

Acorp453

Mój fragment kodu:
Pielegniarka piel = new Pielegniarka("Wanda", "Pigulasta", 3300.00);
int n = 15;
System.out.println("Dane pielęgniarki:");
System.out.println("Imie: "+piel.getName());
System.out.println("Nazwisko: "+piel.getNazwisko());
System.out.println("Wypłata: "+piel.getWyplata());
System.out.println("Liczba godzin nadliczbowych: "+piel.getNadgodziny()+"\n");

Niestety pomimo ustawienia wartości nadgodzin int n = 15; wyświetla mi 0
Bez względu czy zastosuję int n = 15, czy też int nadgodziny = 15, za każdym razem otrzymuję w efekcie wyświetlenia na ekranie nadgodziny=0
Czy może mi ktoś pomóc co robię źle?

Marcin

Chodzi o to, że stworzyłeś zmienną n i dałeś jej wartosc. Ale w zaden sposob nie przekazales tej wartosci do klasy.

Aby to zrobic mozesz:
Opcja 1. Ustawic wartosc obiektu metoda ustalajaca wartosci w obiekcie (to wlasnie ten setter o ktorym mowa), czyli: piel.setNadgodziny(n);
Opcja 2. Nie tworzyc zmiennej n tylko od razu zrobic piel.setNadgodziny(15).

Probujesz wyswietlac zmienna piel.getNadgodziny, ktorej nie dales wartosci. Bo wartosci nadales zmiennej n (lub pozniej zmiennej nadgodziny), a potrzeba bylo wartosc nadac zmiennej piel.nadgodziny (przy uzyciu metody piel.setNadgodziny( liczba );


@Damian na dole...
Jak to dobrze że twój komentarz kogokolwiek obchodzi...

Bogdan

Wyświetla Ci się 0, ponieważ nakazałeś wyświetlenie wartości zwracanej przez piel.getNadgodziny() . A ona jest ustawiona na 0 w konstruktorze.
Zmienna n pomimo nadania jej wartości nie jest w żaden sposób związana z polem nadgodziny obiektu piel.

shiva

Nie wiem jak wam ale dane osob wyswietlaja mi sie na outpucie tylko gdy zmienie w pracowniku pola z private na public..
Da sie to obejsc inaczej?

pecetq

Witam serdecznie,

public Pielegniarka(String imie, String nazwisko, double wyplata) {
super(imie, nazwisko, wyplata);
nadgodziny = 0;
}

public Pielegniarka(String imie, String nazwisko, double wyplata){
this.imie = imie;
this.nazwisko = nazwisko;
this.wyplata = wyplata;
nadgodziny = 0;
}


Te kody nie są sobie równe. Jeśli imie, nazwisko i wypłata są zadeklarowane w nadklasie i w dodatku jako private to jedynym sposobem jest super.imie = imie; ale to i tak nie przejdzie w konstruktorze :)

ichika60h

Witam,
Wywołanie metody nadklasy ma jakiś sens? trudno mi to sobie wyobrazić - można prosić o jakiś przykład?

Amator :(

Witam, jaka tutaj jest roznica pomiedzy klasa nadrzedna, a nadklasa? Niezbyt rozumiem tego ostatniego tam wpisu :/

Lolo

To są synonimy.