390 likes | 539 Views
Dziedziczenie. Marek Serek 132822. 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. Klasy potomne.
E N D
Dziedziczenie Marek Serek 132822
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.
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.
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.
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.
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.
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.
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.
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
Konstruktory klasy bazowej i potomnej Konstruktory dla klasy Punkt3D musimy wiec napisać sami.
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.
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()
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.
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.
Publiczne, prywatne czy chronione? Dostęp do każdego pola i każdej metody może być: • publiczny, • prywatny, • chroniony, • pakietowy.
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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);
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.
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.
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.
Literatura • „Praktyczny kurs Java”- Marcin Lis • „Java po C++”- Jan Bielecki