Wykład 6:
Dziedziczenie
Dziedziczenie
Jeden z filarów obiektowości.
Budowa jednej klasy na bazie drugiej, przez
dodawanie/przesłanianie jej składowych:
●
nad-klasa – klasa bazowa
●
pod-klasa – klasa pochodna od bazowej
Składnia Dziedziczenia
Składnia:
class pod-klasa extends nad-klasa { // treść klasy
}
Każda pod-klasa posiada tylko jedną nad-klasę:
Java nie umożliwia wielo-dziedziczenia.
Przykład: Klasa Bazowa
class A { int i; void pokazi() { System.out.println("i: " + i); } }Przykład: Klasa Pochodna
class B extends A { int j; void pokazj() { System.out.println("j: " + j); } void suma() { System.out.println("i + j: " + (i + j)); } }Przykład: Klasa Testująca
class ProsteDziedziczenie {public static void main(String args[]) { A a = new A();
B b = new B();
Nad-klasa może być używana samodzielnie:
a.i = 10; a.pokazi();
Przykład: Klasa Testująca
Pod-klasa ma dostęp do wszystkich publicznych
składowych swojej nad-klasy:
b.i = 7; b.j = 8; b.pokazi(); b.pokazj(); b.suma(); } }
Dziedziczenie i Składniki Prywatne
Pod-klasa nie ma dostępu do składowych prywatnych
swojej nad-klasy.
Składowa zadeklarowana jako
private
nie jest
dostępna poza klasą w której jest zadeklarowana,
wliczając jej pod-klasy.
Dziedziczenie i Dostęp, Przykład
class A {int i;
private int j;
void ustalij(int x, int y) { i = x; j = y; } } class B extends A { int calosc; void suma() { calosc = i + j; // błąd, j niedostępne } }
Dziedziczenie i Dostęp, Przykład
class DziedziczenieDostep {public static void main(String args[]) { B b = new B(); b.ustalij(10, 12); b.suma(); System.out.println("Suma to " + b.calosc); } }
Kolejny Przykład
class Pudelko { double szerokosc; double wysokosc; double glebokosc; Pudelko() { szerokosc = -1; wysokosc = -1; glebokosc = -1; } double objetosc() {return szerokosc * wysokosc * glebokosc; }
Kolejny Przykład
class PudelkoPlus extends Pudelko { double ciezar; PudelkoPlus(double s, double c) { szerokosc = s; wysokosc = s; glebokosc = s; ciezar = c; } }
Kolejny Przykład
class CiezarPudelka {public static void main(String args[]) {
PudelkoPlus p1 = new PudelkoPlus(10, 5.5); PudelkoPlus p2 = new PudelkoPlus(2, 0.5); double objetosc; objetosc = p1.objetosc(); System.out.println("Pierwsze: " + objetosc); objetosc = p2.objetosc(); System.out.println("Drugie: " + objetosc); } }
Odwołanie do Obiektu Podklasy
Do zmiennej nad-klasy można przypisać odwołanie do
obiektu dowolnej pod-klasy.
class NadKlasa { ... }
class PodKlasa extends NadKlasa { ... } NadKlasa o1;
PodKlasa o2 = new PodKlasa(); o1 = o2
Odwołanie Podklasa, Przykład
class DziedziczenieOdwolanie {public static void main(String args[]) {
PudelkoPlus pp = new PudelkoPlus(10, 0.5); Pudelko p = new Pudelko();
double objetosc; objetosc = pp.objetosc(); System.out.println("Plus: " + objetosc); p = pp; objetosc = p.objetosc(); System.out.println("Pudelko: " + objetosc);
Odwołanie do Obiektu Podklasy
Typ zmiennej, nie obiekt na który wskazuje ta zmienna,
determinuje osiągalne składowe klasy.
To odwołanie byłoby błędem:
//System.out.println(p.ciezar); }
Użycie Super jako Konstruktora
Wywołanie konstruktora nad-klasy:
super(lista-parametrow)
Musi być pierwszą instrukcją konstruktora podklasy:
class NadKlasa { ... }
class PodKlasa extends NadKlasa { PodKlasa(...) {
super(...); ... }
... }
Super jako Konstruktor, Przykład
class Pudelko { double szerokosc; double wysokosc; double glebokosc; Pudelko() {szerokosc = -1; wysokosc= - 1; glebokosc =-1; } Pudelko(Pudelko p) { szerokosc = p.szerokosc; wysokosc = p.wysokosc; glebokosc = p.glebokosc; } ... }
Super jako Konstruktor, Przykład
class PudelkoPlus extends Pudelko {double ciezar; PudelkoPlus() { super(); ciezar = 0; } PudelkoPlus(PudelkoPlus p) { super(p); ciezar = 0; } ... }
Super jako Konstruktor, Przykład
class SuperKonstruktor {public static void main(String args[]) { PudelkoPlus pp1 = new PudelkoPlus();
PudelkoPlus pp2 = new PudelkoPlus(10, 5.5); PudelkoPlus pp3 = new PudelkoPlus(pp2);
double objetosc; objetosc = pp1.objetosc(); System.out.println("Pierwsze " + objetosc); objetosc = pp2.objetosc(); System.out.println("Drugie " + objetosc); objetosc = pp3.objetosc(); System.out.println("Trzecie " + objetosc); } }
Super jako Konstruktor, Przykład
Przesyłamy obiekt pod-klasy:
PudelkoPlus(PudelkoPlus p) { super(p); ciezar = 0;
}
do konstruktora który oczekuje obiekt nad-klasy:
Pudelko(Pudelko p) {
szerokosc = p.szerokosc; wysokosc = p.wysokosc; glebokosc = p.glebokosc; }
Odwołanie do Nad-Klasy przez Super
Odwołanie do elementu nad-klasy:
super.pole
super.metoda()
Stosowany szczególnie gdy składowe pod-klasy
Super, Przykład
class A {int i; }
Przesłonięcie pola w nad-klasie:
class B extends A { int i; B(int a, int b) { super.i = a; i = b; }
Super, Przykład
void pokaz() { System.out.println("nad-klasa: " + super.i); System.out.println("pod-klasa: " + i); } } class SuperPrzesloniecie {public static void main(String args[]) { B b = new B(1, 2);
b.pokaz(); }
Hierarchia Wielo-Poziomowa
class PudelkoPlusPlus extends PudelkoPlus { double koszt;
PudelkoPlusPlus() {
super(); koszt = -1; }
PudelkoPlusPlus(double s, double c, double k) { super(s, c); koszt = k;
}
PudelkoPlusPlus(PudelkoPlusPlus p) { super(p); koszt = p.koszt;
} }
Hierarchia Wielo-Poziomowa
class Przesylka {public static void main(String args[]) { PudelkoPlusPlus pp1 = new PudelkoPlusPlus(10,5,15); PudelkoPlusPlus pp2 = new PudelkoPlusPlus(2, 3, 5); double objetosc; objetosc = pp1.objetosc(); System.out.println("Pierwsze " + objetosc); objetosc = pp2.objetosc(); System.out.println("Drugie " + objetosc); } }
Porządek Wywołań Konstruktorów
W hierarchii klas, najpierw wywołujemy konstruktory
nad-klasy, potem pod-klasy.
Jeżeli
super()
nie jest użyte, wywołuje się domyślny
Porządek Wywołań Konstruktorów
class A { A() { System.out.println("A"); } } class B extends A { B() { System.out.println("B"); } }Porządek Wywołań Konstruktorów
class C extends B { C() { System.out.println("C"); } } class PorzadekKonstruktorow {public static void main(String args[]) { C c = new C();
} }
Przesłanianie Metod
Kiedy metoda pod-klasy ma tą samą nazwę i typ jak
metoda nad-klasy, wtedy mówimy że przesłania ją.
Wersja metody dla nad-klasy zostaje ukryta.
Przesłanianie Metod, Przykład
class A { int i, j; A(int a, int b) { i = a; j = b; } void pokaz() { System.out.println("i i j: " + i + j); } }Przesłanianie Metod, Przykład
class B extends A {int k;
B(int a, int b, int c) { super(a, b); k = c; } void pokaz() { System.out.println("k: " + k); } }
Przesłanianie Metod, Przykład
class Przeslon {public static void main(String args[]) { B b = new B(1, 2, 3);
b.pokaz(); }
Super i Przesłanianie Metod
Wywołanie przesłoniętej metody nad-klasy przez super:
class B extends A { int k;
B(int a, int b, int c) { super(a, b); k = c; } void pokaz() { super.pokaz(); System.out.println("k: " + k); } }
Przesłanianie czy Przeciążenie?
Przesłanianie metod następuje tylko gdy nazwy i
sygnatury dwóch metod są identyczne.
class B extends A { int k;
B(int a, int b, int c) { super(a, b); k = c;
}
void pokaz(String wiadomosc) {
System.out.println(wiadomosc + k); }
Przesłanianie czy Przeciążenie?
class Przeladowanie {public static void main(String args[]) { B b = new B(1, 2, 3);
b.pokaz("Oto k: "); b.pokaz();
} }
Dynamiczne Wywołanie Metod
Przesłanianie umożliwia dynamiczne wywołanie metod.
Odwołanie do przesłoniętej metody przez zmienną
super-klasy: Java ustala wersję metody na podstawie
faktycznego typu obiektu wskazywanego przez zmienną.
Wykonywany kod decyduje się w czasie rzeczywistym,
nie w czasie kompilacji.
Dynamiczne Wywołanie, Przykład
class A { void wywolajMnie() { System.out.println("A"); } } class B extends A { void wywolajMnie() { System.out.println("B"); } } class C extends A { void wywolajMnie() { System.out.println("C"); } }Dynamiczne Wywołanie, Przykład
class DynamiczneWywolanie {public static void main(String args[]) { A a = new A(); B b = new B(); C c = new C(); A z; z = a; z.wywolajMnie(); z = b; z.wywolajMnie(); z = c; z.wywolajMnie(); } }
Polimorfizm
Jeden interfejs, wiele zachowań:
●
nad-klasa definiuje wspólne metody dla pod-klas
●
pod-klasa dostarcza specyficzne implementacje dla
niektórych z tych metod
Swoboda definicji własnych metod przez pod-klase, pod
rygorem zachowania interfejsu dla tych metod.
Kombinacja dziedziczenia i przesłaniania: nad-klasa
opisuje format metod realizowanych przez pod-klasę.
Polimorfizm, Przykład
class Figura { double d1; double d2; Figura(double a, double b) { d1 = a; d2 = b; } double powierzchnia() { System.out.println("Niezdefiniowana"); return 0; } }Polimorfizm, Przykład
class Prostokat extends Figura { Prostokat(double a, double b) { super(a, b); } double powierzchnia() { System.out.println("Prostokat"); return d1 * d2; } }
Polimorfizm, Przykład
class Trojkat extends Figura {Trojkat(double a, double b) { super(a, b); } double powierzchnia() { System.out.println("Trojkat"); return d1 * d2 / 2; } }
Polimorfizm, Przykład
class Polimorfizm {public static void main(String args[]) { Figura f = new Figura(10, 10);
Prostokat p = new Prostokat(9, 5); Trojkat t = new Trojkat(10, 8);
Figura r; r = p;System.out.println(r.powierzchnia()); r = t;System.out.println(r.powierzchnia()); r = f;System.out.println(r.powierzchnia()); } }
Metody Abstrakcyjne
Metoda dla której nie istnieje implementacja.
abstract typ nazwa(lista-parametrow);
Metody:
●
konkretne - mogą być przesłonięte przez pod-klasy
●abstrakcyjne - muszą zostać przesłonięte
Nie wolno definiować abstrakcyjnych konstruktorów,
ani metod statycznych.
Klasy Abstrakcyjne
Klasa która posiada metody abstrakcyjne musi sama
być deklarowana jako abstrakcyjna.
abstract class { ... }
Klasa abstrakcyjna nie posiada obiektów; nie wolno
używać
new
na takiej klasie.
Pod-klasa klasy abstrakcyjnej:
●
implementuje wszystkie metody abstrakcyjne albo
●jest sama deklarowana jako abstrakcyjna.
Klasy Abstrakcyjne
abstract class A {abstract void wywolajMnie(); void wywolajMnieTez() { System.out.println("metoda konkretna"); } } class B extends A { void wywolajMnie() { System.out.print("implementacja metody "); System.out.println("abstrakcyjnej"); } }
Klasy Abstrakcyjne
class KlasaAbstrakcyjna {public static void main(String args[]) { B b = new B();
b.wywolajMnie();
b.wywolajMnieTez(); }
Klasy Abstrakcyjne, Przykład
Abstrakcyjna klasa
Figura
posiada abstrakcyjną
metodę
powierzchnia
:
abstract class Figura { double d1; double d2; Figura(double a, double b) { d1 = a; d2 = b; }
abstract double powierzchnia(); }
Klasy Abstrakcyjne, Przykład
Pod-klasa
Prostokat
dostarcza konkretną
implementację metody
powierzchnia
:
class Prostokat extends Figura { Prostokat(double a, double b) { super(a, b); } double powierzchnia() { System.out.println("Prostokat"); return d1 * d2; } }
Klasy Abstrakcyjne, Przykład
Pod-klasa
Trojkat
dostarcza konkretną
implementację metody
powierzchnia
:
class Trojkat extends Figura { Trojkat(double a, double b) { super(a, b); } double powierzchnia() { System.out.println("Trojkat"); return d1 * d2 / 2; } }
Klasy Abstrakcyjne, Przykład
class AbstrakcyjnaFigura {public static void main(String args[]) {
Nie wolno tworzyć obiektów klasy abstrakcyjnej:
Figura f = new Figura(10, 10);
Prostokat p = new Prostokat(9, 5); Trojkat t = new Trojkat(10, 8);
Wolno tworzyć odwołania do klasy abstrakcyjnej:
Figura r;
r=p;System.out.println(r.powierzchnia()); r=t;System.out.println(r.powierzchnia()); }
Final Zapobiega Przesłanianiu
Metodę deklarowaną jako
final
w nad-klasie nie
wolno przesłaniać w pod-klasie:
class A {
final void meth() {
System.out.println("Metoda final"); } } class B extends A { void meth() { System.out.println("Nielegalne!"); } }
Final Zapobiega Przesłanianiu
Dwa rodzaje wywołań metod:
●
wczesne – wywołanie ustalone w trakcie kompilacji
●późne – wywołanie ustalane w trakcie wykonania
Jako że metoda
final
nie może zostać przesłonięta,
podlega wczesnemu wywołaniu.
Final Zapobiega Dziedziczeniu
Klasa deklarowana jako
final
nie posiada potomków,
nie wolno po niej dziedziczyć.
final class A { ... }
Ta klasa jest nielegalna:
Klasa Object
Klasa
Object
jest nad-klasą wszystkich klas w Javie.
Zmienna typu
Object
może odwoływać się do obiektu
dowolnej klasy, jak też do dowolnej tablicy.
Metody deklarowane przez
Object:
●
Object clone()
tworzy obiekt który jest
idealną kopią danego obiektu
●
boolean equals(Object object)
ustala czy
Klasa Object
●
void finalize()
wywołana zanim nieużywany
obiekt jest niszczony przez śmieciarkę
●
final Class getClass()
uzyskuje dostęp do
klasy obiektu w trakcie wykonania
●
int hashCode()
zwraca kod haszujący dla
danego obiektu
●
final void notify()
podejmuje ponowne
wykonanie wątku który czeka na danym obiekcie
●
final void notifyAll()
podejmuje wykonanie
Klasa Object
●
String toString()
zwraca ciąg znaków który
opisuje dany obiekt, jest wywoływany przez
●
final void wait()
czeka na inny wątek
●