Dziedziczenie

Download Report

Transcript Dziedziczenie

Slide 1

Dziedziczenie

Marek Serek 132822


Slide 2

Dziedziczenie
Dziedziczenie to jeden z fundamentów
programowania obiektowego. Umożliwia
sprawne i łatwe wykorzystywanie już raz
napisanego kodu czy budowanie hierarchii
klas, przejmujących swoje właściwości.


Slide 3

Klasy potomne
W tej części zostaną przedstawione
podstawy dziedziczenia, czyli budowanie
nowych klas na bazie już istniejących.
Każda taka klasa przyjmuje zachowanie i
właściwości klasy bazowej. Zobaczymy, jak
tworzy się klasy potomne, jakie
podstawowe zależności występują miedzy
klasą bazową, a potomną oraz jak
zachowują się konstruktory w przypadku
dziedziczenia.


Slide 4

Dziedziczenie
Załóżmy, że mamy stworzoną klasę Punkt, która
przechowuje informacje o współrzędnej punktu
na płaszczyźnie i która posiada dodatkowe
metody, które pozwalają na ustawienie i
pobranie tych współrzędnych. Zastanówmy się
teraz co byśmy zrobili, gdybyśmy potrzebowali
określać położenie punktu nie w dwóch, ale w
trzech wymiarach, czyli do współrzędnych x i y
trzeba było dodać współrzędną z.


Slide 5

Dziedziczenie
Pomysł który się od razu nasuwa, jest napisanie dodatkowej
klasy, np. o nazwie Punkt3D, w postaci:
class Punkt3D
{
int x;
int y;
int z;
}
Do tej klasy należałoby dalej dopisać zestaw metod, które
znajdowały się w klasie Punkt, takich jak pobierzX,
pobierzyY, ustawX, ustawY itd. Oraz dodatkowe metody
operujące na współrzędnej z.


Slide 6

Dziedziczenie
Aby się nie powtarzać w już raz napisanym
kodzie należy zastosować dziedziczenie.
Ponieważ klasa Punkt3D jest pewnego rodzaju
rozszerzeniem klasy Punkt. Rozszerza je o
dodatkowe możliwości (pola, metody)
pozostawiając stare właściwości bez zmian.
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.
Powiemy wówczas, że klasa Punkt3D dziedziczy z
klasy Punkt, czyli przejmuje jej pola i metody
oraz dodaje swoje własne.


Slide 7

Dziedziczenie
W Javie dziedziczenie jest wyrażone za pomocą
słowa extends, a cała definicja schematycznie
wygląda następująco:
class klasa_potomna extends klasa_bazowa
{
//ciało klasy
}
Zapis taki oznacza, że klasa potomna dziedziczy
z klasy bazowej.


Slide 8

Przykład


Slide 9

Dziedziczenie
Klasa Punkt3D,
dziedziczy z klasy
Punkt dwa pola x i y
oraz wszystkie
metody: pobierzX ,
pobierzY, ustawX,
ustawY. Oprócz tego
zawiera własne pole z
i dwie metody
pobierzZ i ustawZ.


Slide 10

Konstruktory klasy bazowej i potomnej
Klasa Punkt3D posiada już metody operujące na
polu z, tzn. ustawiające oraz pobierające jego
wartość, brakuje jej jednak konstruktorów.
Przypomnijmy, że w klasie Punkt napisaliśmy aż
trzy konstruktory:
• Bezparametrowy, ustawiający wartość
wszystkich pól na 1
• Dwuparametrowy, przyjmujący dwie wartości
typu int
• Jednoparametrowy, przyjmujący obiekt klasy
Punkt


Slide 11

Konstruktory klasy bazowej i potomnej
Konstruktory dla klasy
Punkt3D musimy wiec
napisać sami.


Slide 12

Konstruktory klasy bazowej i potomnej
Jeśli przyjrzymy się
dokładnie napisanym
przed chwilą
konstruktorom,
zauważymy, że w
znacznej części ich
kod dubluje się z
kodem konstruktorów
klasy Punkt.


Slide 13

Konstruktory klasy bazowej i potomnej
Lepiej będzie zatem
wykorzystać konstruktor
klasy Punkt w klasie
Punkt3D lub ogólniej
konstruktor klasy bazowej
w konstruktorze klasy
potomnej. Do tego celu
służy specjalna
konstrukcja. Dokładniej w
konstruktorze klasy
potomnej należy wywołać
metodę super()


Slide 14

Konstruktory klasy bazowej i potomnej
Jeśli metodzie super
przekażemy
parametry, zostanie
wywołany konstruktor
klasy bazowej, który
tym parametrom
odpowiada. Do
praktycznego
zobrazowania takiej
konstrukcji doskonale
nadaje się klasa
Punkt3D.


Slide 15

Specyfikatory dostępu i pakiety
Specyfikatory dostępu pełnią ważna rolę w
Javie, pozwalają bowiem na określenie
swoistego rodzaju praw dostępu do
składowych klas, a także do samych klas.
W Javie przed każdym polem i metodą
może, a czasami nawet powinien, pojawić
się tak zwany specyfikator dostępu.


Slide 16

Publiczne, prywatne czy chronione?
Dostęp do każdego pola i każdej metody
może być:
• publiczny,
• prywatny,
• chroniony,
• pakietowy.


Slide 17

Publiczne, prywatne czy chronione?
Domyślnie, jeżeli przed składową klasy nie
występuje żadne określenie, dostęp jest
pakietowy, co oznacza, że dostęp do tej
składowej mają wszystkie klasy pakietu, w
którym się one znajdują. Dostęp publiczny
jest określony słowem public, dostęp
prywatny słowem private, a dostęp
chroniony słowem protected.


Slide 18

Dostęp prywatny - private
Składowe oznaczone słowem private to
takie, które są dostępne jedynie z wnętrza
danej klasy. To oznacza, że wszystkie
metody danej klasy mogą je dowolnie
odczytywać i zapisywać, natomiast żadna
inna klasa nie może ani ich odczytać, ani
zapisać. Dla innych klas są po prostu
niewidoczne.
Należy pamiętać że składowe prywatne nie
będą dostępne nawet dla klas potomnych.


Slide 19

Przykład


Slide 20

W jaki sposób się odwołać do pola
prywatnego?
Aby odwołać się do pola
prywatnego wystarczy
zatem, jeśli napiszemy
publiczne metody
pobierające i
ustawiające pola
prywatne. Wtedy
będziemy mogli
wykonywać na nich
operacje.


Slide 21

Dostęp chroniony - protected
Składowe klasy oznaczone słowem protected to
składowe chronione. Są one dostępne jedynie dla
metod danej klasy, klas potomnych oraz klas z
tego samego pakietu. Oznacza to, że jeśli mamy
przykładową klasę Punkt, w której znajdzie się
chronione pole o nazwie x, to w klasie pochodnej
Punkt3D, również będziemy mogli odwoływać się
do pola x. Jednak dla każdej innej klasy, która
nie dziedziczy z Punkt, pole x będzie
niedostępne.


Slide 22

Czemu ukrywamy wnętrze klasy?
Zabraniamy bezpośredniego dostępu do
niektórych składowych klas, stosując
modyfikatory private i protected, aby
ukryć implementacje wnętrza klasy.
Programista, projektując daną klasę
udostępnia na zewnątrz (innym
programom) pewien interfejs służący do
posługiwania się obiektami tejże klasy.


Slide 23

Czemu ukrywamy wnętrze klasy?
Czyli określa sposób, w jaki można
korzystać z danego obiektu. To co
znajduje się wewnątrz, jest ukryte,
dzięki temu można całkowicie zmienić
wewnętrzną konstrukcję klasy, nie
zmieniając zupełnie sposobu korzystania
z niej.


Slide 24

Przeciążenia metody a dziedziczenie
Przeciążenie metod, czyli możliwość
umieszczenia w jednej klasie kilku metod
o tej samej nazwie, różniących się
argumentami. Pytanie, jak ma się to do
dziedziczenia, czyli czy możemy
przeciążać metody klasy bazowej w klasie
potomnej? Odpowiedź brzmi-tak. Jeśli
mamy np.. Klasę o nazwie A, a w niej
bezargumentową metodę f, czyli klasę w
postaci:


Slide 25

Przeciążenia metody a dziedziczenie
I z tej klasy
wyprowadzimy klasę
pochodną o nazwie B,
to w klasie B możemy
zdefiniować
przeciążoną metodę f,
np. przyjmującą
jeden typ int.


Slide 26

Przeciążenia metody a dziedziczenie
Czyli w klasie A została
zdefiniowana
bezargumentowa metoda
o nazwie f. Jej zadaniem
jest wyświetlenie nazwy
klasy, w której została
zdefiniowana, na ekranie.
W klasie B, dziedziczącej
z klasy A, również została
zdefiniowana metoda o
nazwie f, ale przyjmująca
jeden argument typu int.


Slide 27

Przeciążenia metody a dziedziczenie
Jedynym zadaniem metody f z klasy B jest
również wypisanie na ekran nazwy klasy, w
której została zdefiniowana.
Przeciążenie metod w przypadku
dziedziczenia działa tak samo, jak gdyby
odbyło się to w jednej klasie.


Slide 28

Program ilustrujący działanie przeciązenia
Wywołanie trzecie b.f()
oraz czwarte b.f(0) są
prawidłowe. W klasie B
istnieje zarówno
bezargumentowa
metoda f odziedziczona
z klasy A, jak i
przeciążona metoda o
nazwie f (zdefiniowana
w klasie B), która
przyjmuje argument
typu int.


Slide 29

Przesyłanie pól i metod


Przesyłanie metod
Wiemy już, że w klasach potomnych
można przeciążyć metody zdefiniowane w
klasie bazowej, jest to wręcz zgodne z
logiką i intuicją, dziwiłby fakt, gdyby
takiej możliwości nie było.
Rozważmy przypadek, kiedy w klasie
potomnej ponownie zdefiniujemy metodę
o takiej samej nazwie i takich samych
argumentach jak w klasie bazowej.


Slide 30

Przesyłanie metod
W klasie A znajduje
się bezargumentowa
metoda o nazwie f,
która wyświetla na
ekranie nazwę klasy,
w której została
zdefiniowana. Klasa B
dziedziczy z klasy A,
zgodnie z zasadami
dziedziczenia,
przyjmuje więc
metodę f z klasy A.


Slide 31

Przesyłanie metod
Tymczasem w klasie B została ponownie
zadeklarowana bezargumentowa metoda f
(również wyświetlająca nazwę klasy, w której
została zdefiniowana, czyli tym razem klasy B).
Wydawać by się mogło, że w takim wypadku
wystąpi konflikt nazw (dwukrotne
zadeklarowanie metody f).
Otóż zasada jest następująca: jeśli w klasie
bazowej i klasie pochodnej występuje metoda o
tej samej nazwie i argumentach, metoda z klasy
bazowej jest przesłaniana.


Slide 32

Przesyłanie metod
Czyli w obiektach klasy bazowej będzie
obowiązywała metoda z klasy bazowej, a
w obiektach klasy pochodnej metoda z
klasy pochodnej.
Prześledźmy co pojawi się na ekranie po
uruchomieniu klasy Main, która korzysta z
obiektów klasy A i B.


Slide 33

Przesyłanie metod
Na ekranie pojawi się
oczywiście najpierw znak
A, a następnie znak B.
Skoro bowiem obiekt a
jest klasy A, to wywołanie
a.f() powoduje wywołanie
metody f z klasy A, czyli
wyświetlenie znaku A. Z
kolei obiekt b jest klasy
B, zatem wywołanie b.f()
powoduje uruchomienie
metody f z klasy B i
wyświetlenie znaku B.


Slide 34

Przesyłanie metod
Pojawić może się w tym miejscu pytanie,
czy jest w takim razie możliwe wywołanie
w klasie pochodnej metody przesłoniętej z
klasy bazowej.
Odwołanie takie jest możliwe, podobnie
jak przy wywołaniu konstruktora klasy
bazowej, korzystamy z instrukcji super w
postaci:
super.nazwa_metody(argumenty);


Slide 35

Przykład


Slide 36

Przesyłanie pól
Pola klasy bazowej są przesyłane w sposób
analogiczny jak w przypadku metod. Czyli,
jeśli w klasie pochodnej zdefiniujemy pole
o takiej samej nazwie jak w klasie
bazowej, bezpośrednio dostępne będzie
tylko pole klasy pochodnej.


Slide 37

Przesyłanie pól
Należy sobie
uświadomić, że każdy
obiekt klasy B zawiera
teraz DWA pola o
nazwie liczba. Jedno z
nich pochodzi z klasy
A, drugie z klasy B.
Tym polom można z
kolei przypisywać
różne wartości.


Slide 38

Przesyłanie pola
Oczywiście, jeśli mamy obiekt takiej klasy
B, to z dowolnej klasy zewnętrznej
jesteśmy w stanie 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ę do
drugiego pola liczba.


Slide 39

Literatura
• „Praktyczny kurs Java”- Marcin Lis
• „Java po C++”- Jan Bielecki