Dziedziczenie
Monika Wrzosek (IM UG) Programowanie obiektowe 92 / 122
Dziedziczenie
To jeden z fundamentów programowania obiektowego.
To budowanie nowych klas na bazie już istniejących. Każda taka nowa klasa przejmuje zachowanie i właściwości klasy bazowej.
- Na poprzednich wykładach powstała klasa Punkt, która przechowywała informację o współrzędnych punktu na płaszczyźnie.
- Załóżmy, że zachodzi potrzeba określenia położenia punktu w trzech wymiarach.
Moglibyśmy utworzyć nową klasę, np. o nazwie Punkt3D:
p u b l i c c l a s s Punkt3D { i n t x , y , z ;
}
- Do tej klasy należałoby dopisać pełny zestaw metod, które znajdowały się w klasie Punkt, oraz dodatkowe metody operujące na współrzędnej z. W dużej części powtórzylibyśmy już raz napisany kod.
- Klasa Punkt3D jest rozszerzeniem klasy Punkt. Zamiast więc pisać całkiem od
nowa klasę Punkt3D, lepiej spowodować, aby przejęła ona wszystkie możliwości
klasy Punkt, wprowadzając dodatkowo swoje własne pola i metody. Jest to tak
zwane dziedziczenie.
Dziedziczenie
- Powiemy, że klasa Punkt3D dziedziczy po klasie Punkt, czyli przejmuje jej pola i metody oraz dodaje swoje własne.
- Klasę Punkt nazwiemy klasą bazową/nadrzędną/nadklasą (ang. superclass).
- Klasę Punkt3D nazwiemy klasą potomną/pochodną/podrzędną/podklasą (ang.
subclass).
Składnia
class klasa_potomna extends klasa_bazowa {
treść klasy }
Przykład:
p u b l i c c l a s s Punkt3D e x t e n d s Punkt { i n t z ;
}
Taki zapis oznacza, że klasa Punkt3D przejęła wszystkie właściwości klasy Punkt, a dodatkowo otrzymała pole typu int o nazwie z.
Monika Wrzosek (IM UG) Programowanie obiektowe 94 / 122
p u b l i c c l a s s Punkt { i n t x , y ;
i n t p o b i e r z X ( ) { r e t u r n x ; }
i n t p o b i e r z Y ( ) { r e t u r n y ; }
v o i d ustawX (i n t wspX ) { x = wspX ;
}
v o i d ustawY (i n t wspY ) { y = wspY ;
}
v o i d ustawXY (i n t wspX , i n t wspY ) { x = wspX ;
y = wspY ; }
v o i d w y s w i e t l ( ) {
S y s t e m . o u t . p r i n t l n (" x = " + x + " , y = " + y ) ; }
}
p u b l i c c l a s s Punkt3D e x t e n d s Punkt { i n t z ;
}
p u b l i c c l a s s Main {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Punkt3D P = new Punkt3D ( ) ;
P . ustawX ( 1 0 0 ) ; P . ustawY ( 2 0 0 ) ; P . w y s w i e t l ( ) ;
S y s t e m . o u t . p r i n t l n (" z = " + z ) ;
P . ustawXY ( 3 , 4 ) ; P . w y s w i e t l ( ) ;
S y s t e m . o u t . p r i n t l n (" z = " + z ) ;
x = 1 0 0 , y = 200 z = 0 ;
x = 3 , y = 4 z = 0 ;
Klasa Punkt3D nie jest jednak w pełni funkcjonalna, należałoby dopisać metody operujące na nowym polu z, np. ustawZ, pobierzZ, ustawXYZ.
Monika Wrzosek (IM UG) Programowanie obiektowe 96 / 122
p u b l i c c l a s s Punkt3D e x t e n d s Punkt { i n t z ;
i n t p o b i e r z Z ( ) { r e t u r n z ; }
v o i d ustawZ (i n t wspZ ) { z = wspZ ;
}
v o i d ustawXYZ (i n t wspX , i n t wspY , i n t wspZ ) { x = wspX ;
y = wspY ; z = wspZ ; }
v o i d ustawXYZ ( Punkt3D A) {
x = A . x ; y = A . y ; z = A . z ; }
v o i d ustawXYZ ( Punkt P , i n t wspZ ) { x = P . x ;
y = P . y ; z = wspZ ; }
}
Konstruktor klasy potomnej
p u b l i c c l a s s Punkt3D e x t e n d s Punkt { i n t z ;
Punkt3D ( ) { x = 1 ; y = 1 ; z = 1 ; }
Punkt3D (i n t wspX , i n t wspY , i n t wspZ ) { x = wspX ;
y = wspY ; z = wspZ ; }
Punkt3D ( Punkt3D A) { x = A . x ;
y = A . y ; z = A . z ; }
/∗ p o z o s t a ł e metody k l a s y Punkt3D ∗/
}
- Kod tych konstruktorów w znacznej części dubluje się z kodem konstruktorów klasy Punkt.
- Czy można wykorzystać konstruktor klasy Punkt w klasie Punkt3D; ogólniej - konstruktor klasy bazowej w konstruktorze klasy potomnej?
Tak, służy do tego metoda super().
Monika Wrzosek (IM UG) Programowanie obiektowe 98 / 122
- Metoda super() to wywołanie konstruktora klasy bazowej.
- Ważne jest, aby była ona pierwszą instrukcją konstruktora klasy potomnej. Próba umieszczenia tej instrukcji w innym miejscu spowoduje błąd kompilacji.
p u b l i c c l a s s Punkt3D e x t e n d s Punkt { i n t z ;
Punkt3D ( ) { s u p e r( ) ; z = 1 ; }
Punkt3D (i n t wspX , i n t wspY , i n t wspZ ) { s u p e r( wspX , wspY ) ;
z = wspZ ; }
Punkt3D ( Punkt3D A) { s u p e r(A ) ;
z = A . z ; }
}
- W pierwszym konstruktorze wywołujemy bezargumentową metodę super, która powoduje wywołanie bezargumentowego konstruktora klasy bazowej. Taki konstruktor (bezargumentowy) istnieje w klasie Punkt, więc konstrukcja jest OK.
- W drugim konstruktorze wywołujemy metodę super, przekazując jej dwa parametry
typu int. Ponieważ w klasie Punkt istnieje konstruktor dwuargumentowy przyjmujący
dwie wartości typu int, ta konstrukcja też jest OK.
p u b l i c c l a s s Punkt3D e x t e n d s Punkt { i n t z ;
Punkt3D ( ) { s u p e r( ) ; z = 1 ; }
Punkt3D (i n t wspX , i n t wspY , i n t wspZ ) { s u p e r( wspX , wspY ) ;
z = wspZ ; }
Punkt3D ( Punkt3D A) { s u p e r(A ) ;
z = A . z ; }
}
- Trzeci konstruktor przyjmuje jeden argument typu (klasy) Punkt3D, ale w klasie Punkt nie ma konstruktora, który przyjmowałby argument tego typu! Jest konstruktor:
Punkt ( Punkt P ) { x = P . x ; y = P . y ; }
ale przyjmuje on argument typu Punkt, a nie Punkt3D. Czy wystąpi więc błąd kompilacji?
- Nie. Jeśli oczekiwany jest argument klasy X, a podany zostanie argument klasy Y, która jest klasą potomną dla X, błędu nie będzie (nastąpi tzw. rzutowanie obiektu).
Monika Wrzosek (IM UG) Programowanie obiektowe 100 / 122
Przeciążanie metod a dziedziczenie
p u b l i c c l a s s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f ( ) z k l a s y A") ; }}
p u b l i c c l a s s B e x t e n d s A { p u b l i c v o i d f (i n t l i c z b a ) {
S y s t e m . o u t . p r i n t l n (" Metoda f ( i n t ) z k l a s y B") ; }
}
p u b l i c c l a s s Main {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { A a = new A ( ) ;
B b = new B ( ) ;
// p r a w i d ł owe wywo ł a n i e , w k l a s i e A i s t n i e j e metoda f ( ) : a . f ( ) ; // Wynik : Metoda f ( ) z k l a s y A
// n i e p r a w i d ł owe wywo ł a n i e , w k l a s i e A n i e ma metody f ( i n t ) : // a . f ( 0 ) ;
// oba p r a w i d ł owe , w k l a s i e B i s t n i e j ą metody f ( ) i f ( i n t ) : b . f ( ) ; // Wynik : Metoda f ( ) z k l a s y A
b . f ( 0 ) ; // Wynik : Metoda f ( i n t ) z k l a s y B }
}
Przesłanianie metod
- Wiemy, że w klasach potomnych można przeciążać metody klasy bazowej.
- Co się jednak stanie, kiedy w klasie potomnej ponownie zdefiniujemy metodę o takiej samej nazwie i takich samych argumentach jak w klasie bazowej?
p u b l i c c l a s s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f z k l a s y A") ; }}
p u b l i c c l a s s B e x t e n d s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f z k l a s y B") ; }}
- Klasa potomna B przejmuje z klasy A funkcję f (), ale sama również ją definiuje.
- Wydawać by się mogło, że w takim wypadku wystąpi konflikt nazw (dwukrotne zadeklarowanie metody f ()), kompilator nie zgłasza błędów.
Przesłanianie/przykrywanie/nadpisywanie (ang. overriding )
Jeśli w klasie bazowej i pochodnej są metody o tej samej nazwie i argumentach, metoda z klasy bazowej jest przesłaniana.
W obiektach klasy bazowej będzie zatem obowiązywała metoda z klasy bazowej, a w obiektach klasy pochodnej - metoda z klasy pochodnej.
Monika Wrzosek (IM UG) Programowanie obiektowe 102 / 122
Przesłanianie metod
p u b l i c c l a s s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f z k l a s y A") ; }
}
p u b l i c c l a s s B e x t e n d s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f z k l a s y B") ; }
}
p u b l i c c l a s s Main {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { A a = new A ( ) ;
B b = new B ( ) ; a . f ( ) ;
b . f ( ) ; }
}
Metoda f z k l a s y A Metoda f z k l a s y B
Czy możliwe jest wywołanie w klasie pochodnej przesłoniętej metody z klasy bazowej?
p u b l i c c l a s s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f z k l a s y A") ; }
}
p u b l i c c l a s s B e x t e n d s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" Metoda f z k l a s y B") ; }
}
Czy w obiekcie klasy B można wywołać metodę f pochodzącą z klasy A?
Odwołanie do przesłoniętej metody klasy bazowej uzyskujemy dzięki wywołaniu:
super.nazwa_metody(argumenty);
Monika Wrzosek (IM UG) Programowanie obiektowe 104 / 122
p u b l i c c l a s s A { p u b l i c v o i d f ( ) {
S y s t e m . o u t . p r i n t l n (" K l a s a A") ; }
}
p u b l i c c l a s s B e x t e n d s A {
// t a metoda p r z e s ł a n i a metod ę f z k l a s y A ,
// a l e wywo ł u j e r ó w n i e ż p r z e s ł o n i ę t ą metod ę f z k l a s y A : p u b l i c v o i d f ( ) {
s u p e r. f ( ) ;
S y s t e m . o u t . p r i n t l n (" K l a s a B") ; }
// t a metoda wywo ł u j e metod ę f z k l a s y B : p u b l i c v o i d g ( ) {
f ( ) ; }
// t a metoda wywo ł u j e metod ę f z k l a s y A : p u b l i c v o i d h ( ) {
s u p e r. f ( ) ; }
}
p u b l i c c l a s s Main {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { A a = new A ( ) ;
B b = new B ( ) ;
S y s t e m . o u t . p r i n t l n (" Wynik wywo ł a n i a a . f ( ) : ") ; a . f ( ) ;
S y s t e m . o u t . p r i n t l n (" \ nWynik wywo ł a n i a b . f ( ) : ") ; b . f ( ) ;
S y s t e m . o u t . p r i n t l n (" \ nWynik wywo ł a n i a b . g ( ) : ") ; b . g ( ) ;
S y s t e m . o u t . p r i n t l n (" \ nWynik wywo ł a n i a b . h ( ) : ") ; b . h ( ) ;
} }
Wynik wywo ł a n i a a . f ( ) : K l a s a A
Wynik wywo ł a n i a b . f ( ) : K l a s a A
K l a s a B
Wynik wywo ł a n i a b . g ( ) : K l a s a A
K l a s a B
Wynik wywo ł a n i a b . h ( ) : K l a s a A
Monika Wrzosek (IM UG) Programowanie obiektowe 106 / 122
- W klasie potomnej B:
1. zdefiniowano metodę f , która przesłania metodę f z klasy A. Jednak w tej metodzie jest wywoływana (za pomocą składni super ) metoda przesłonięta.
Dopiero po tym wywołaniu zostaje wyświetlona nazwa klasy B.
2. Metoda g wywołuje w standardowy sposób metodę f (czyli będzie to metoda f z klasy B).
3. Metoda h korzysta ze składni super: super.f(), zatem wywołuje metodę f zdefiniowaną w klasie A.
- W klasie Main:
1. wywoływana jest metoda f obiektu klasy A, zatem na ekranie pojawia się napis Klasa A;
2. wywoływana jest metoda f obiektu b. Ponieważ najpierw wywołuje ona metodę f klasy A, a potem wyświetla nazwę klasy B, jej wynikiem są dwa teksty:
Klasa A i Klasa B;
3. wywoływana jest metoda g obiektu b, której jedynym zadaniem jest wywołanie metody f z klasy B. Wynik jest więc identyczny jak w punkcie 2;
4. wywoływana jest metoda h obiektu b. Jest w niej uruchamiana (za pomocą
super ) metoda f z klasy A, więc wyświetla się tekst Klasa A.
Przesłanianie pól
Pola klas bazowych są przesłaniane tak samo jak w przypadku metod.
p u b l i c c l a s s A { p u b l i c i n t l i c z b a ; }
p u b l i c c l a s s B e x t e n d s A { p u b l i c i n t l i c z b a ;
- W klasie A zostało zdefiniowane pole o nazwie liczba i typie int.
- W klasie B, która dziedziczy po A, ponownie zostało zadeklarowane pole o takiej samej nazwie i identycznym typie.
- Każdy obiekt klasy B będzie więc zawierał DWA pola o nazwie liczba: jedno pochodzące z klasy A, drugie z B.
- Jeśli mamy obiekt klasy B, to z dowolnej klasy zewnętrznej możemy dostać się jedynie do pola liczba zdefiniowanego w klasie B. Jednak już z wnętrza klasy B za pomocą składni super możemy odwołać się również do drugiego pola liczba (pochodzącego z klasy A).
Monika Wrzosek (IM UG) Programowanie obiektowe 108 / 122
p u b l i c c l a s s A { p u b l i c i n t l i c z b a ; }
p u b l i c c l a s s B e x t e n d s A { p u b l i c i n t l i c z b a ;
p u b l i c v o i d u s t a w L i c z b a A (i n t l i c z b a ) { s u p e r. l i c z b a = l i c z b a ;
}
p u b l i c v o i d u s t a w L i c z b a B (i n t l i c z b a ) { t h i s. l i c z b a = l i c z b a ;
}
p u b l i c i n t p o b i e r z L i c z b a A ( ) { r e t u r n s u p e r. l i c z b a ;
}
p u b l i c i n t p o b i e r z L i c z b a B ( ) { r e t u r n l i c z b a ;
} }
p u b l i c c l a s s Main {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( ) ;
b . u s t a w L i c z b a A ( 0 ) ; b . u s t a w L i c z b a B ( 1 ) ;
S y s t e m . o u t . p r i n t (" Warto ś ć p r z e s ł o n i ę t e g o p o l a l i c z b a : ") ; S y s t e m . o u t . p r i n t l n ( b . p o b i e r z L i c z b a A ( ) ) ;
S y s t e m . o u t . p r i n t (" Warto ś ć p o l a l i c z b a z k l a s y B : ") ; S y s t e m . o u t . p r i n t l n ( b . p o b i e r z L i c z b a B ( ) ) ;
} }
Warto ś ć p r z e s ł o n i ę t e g o p o l a l i c z b a : 0 Warto ś ć p o l a l i c z b a z k l a s y B : 1
Monika Wrzosek (IM UG) Programowanie obiektowe 110 / 122