Java (2)
Plan wykładu
◮
Dziedziczenie
◮
Słowo kluczowe final
◮
Polimorfizm
◮
Interfejsy
Dziedziczenie
◮
mechanizm dziedziczenia jest integralną częścią Javy (w
zasadzie nie można z niego nie korzystać!).
◮
klasa bazowa wskazywana jest po słowie extends
class KlasaBazowa {
...
}
class KlasaPochodna extends KlasaBazowa {
...
}
◮
jeżeli klasa bazowa nie zostanie jawnie wskazana, jest nią klasa
Object
◮
zatem każda klasa jest potomkiem klasy Object
Dziedziczenie
klasa pochodna
◮
klasa pochodna dziedziczy atrybuty i metody klasy bazowej
(poza konstruktorami)
◮
w klasie pochodnej dostępne są wszystkie składowe klasy
bazowej oznaczone jako public oraz protected. Niedostępne są
składowe oznaczone jako private (dostęp przyjazny
(pakietowy) nie ma związku z hierarchią klas)
◮
w klasie pochodnej można wrowadzić nowe atrybuty i metody
oraz zredefiniować metody dziedziczone
◮
przykład:
class KlasaBazowa {
private int i;
public void metoda1() { ... }
public void metoda2() { ... }
}
class KlasaPochodna extends KlasaBazowa {
int j;
// dodatkowy atrybut
public void metoda1() { ... } // redefinicja
public void metoda3() { ... } // nowa metoda
}
Dziedziczone
super
◮
słowo kluczowe super pozwala w klasie pochodnej wywoływać
składowe z klasy bazowej, jeśli ich nazwy zostały
zredefiniowane w klasie pochodnej
class KlasaBazowa {
public void powitaj() {
System.out.print("Dzień dobry!");
}
}
class KlasaPochodna extends KlasaBazowa {
public void powitaj() {
super.powitaj();
System.out.print("Cześć! I Czołem!");
}
}
Dziedziczenie
konstruktory
◮
w celu poprawnej inicjalizacji klas, Java oferuje mechanizm
automatycznego wywoływania konstruktorów domniemanych
(bezargumentowych) klas nadrzędnych podczas wywoływania
konstruktora klasy bazowej
◮
jeżeli klasa pochodna nie zawiera konstruktorów
domniemanych lub gdy konieczne jest wywołanie konstruktora
klasy bazowej z parametrami konieczne jest wywołanie
konstruktora nadklasy przy pomocy słowa kluczowego super;
wywołanie to musi być pierwszą instrukcją w ciele konstruktora
Dziedziczenie
konstruktory - przykład 1
public class Profesor extends Pracownik {
Profesor() {
System.out.println("Konstruktor klasy Profesor");
}
public static void main(String[] args) {
Profesor x = new Profesor();
}
}
class Osoba {
Osoba() {
System.out.println("Konstruktor klasy Osoba");
}
}
class Pracownik extends Osoba {
Pracownik() {
System.out.println("Konstruktor klasy Pracownik");
}
}
Dziedziczenie
Konstruktory - przykład 2
public class Zebra extends Ssak {
Zebra() {
super(11);
System.out.println("Konstruktor klasy Zebra");
}
public static void main(String[] args) {
Zebra x = new Zebra();
}
}
class Zwierze {
Zwierze(int i) {
System.out.println("Konstruktor klasy Zwierze");
}
}
class Ssak extends Zwierze {
Ssak(int i) {
super(i);
System.out.println("Konstruktor klasy Ssak");
}
}
Słowo kluczowe final
definicje ostateczne
Poprzedzenie definicji słowem final oznacza, że definicja ta jest
ostateczna, rzecz definiowana nie może być
zmieniona/zredefiniowana.
Słowo kluczowe final
zmienne ostateczne
◮
Java, poprzez użycie słowa kluczowego final, przed deklaracją
zmiennej zezwala na tworzenie zmiennych, których wartości raz
zainicjowane nie mogą zostać zmodyfikowane:
◮
W przypadku użycia słowa kluczowego final przy referencjach
nie ma możliwości przypisania takiej referencji innego obiektu
(sam obiekt może być modyfikowany)
final int i = 1;
final kp = new KlasaPrzykladowa();
Słowo kluczowe final
atrybuty ostateczne
◮
atrybuty ostateczne mogą być inicjalizowane w deklaracji
◮
atrybuty ostateczne nie zainicjowane w deklaracji muszą zostać
zainicjowane we wszystkich konstruktorach danej klasy (dzięki
temu możliwe jest posiadanie atrybutów ostatecznych o
różnych wartościach w różnych obiektach klasy)
◮
przykład
class KlasaPrzykladowa {
final int id;
static int nastepnyid = 1;
KlasaPrzykladowa() {
id = nastepnyid++;
}
}
Słowo kluczowe final
stałe (ostateczne atrybuty statyczne)
◮
połącznie słowa kluczowego final i static pozwala na
definiowanie stałych publicznych
◮
dzięki stałym publicznym możliwe jest udostępnianie stałych
bez konieczności tworzenia obiektów danej klasy.
◮
przykład
public class Math {
...
public static final double PI = 3.14159365358979323846;
...
}
Słowo kluczowe final
metody ostateczne
◮
użycie słowa kluczowego final przed definicją metody
uniemożliwia zredefiniowanie tej metody w klasach pochodnych
class KlasaPrzykladowa {
public final metoda1() {...}
}
◮
metody ostateczne wywoływane są w trybie inline (rozwiązanie
to znacząco obniża czas realizacji danej metody)
◮
kompilator Javy decyduje ostatecznie o tym, czy metoda
ostateczna zostanie wywołana w trybie inline; dzieje się tak dla
metod o małej ilości kodu
Słowo kluczowe final
argumenty ostateczne
użycie słowa kluczowego final przed deklaracją argumentu
uniemożliwia dokonywanie zmiany wartości tego argumentu
wewnątrz metody
Słowo kluczowe final
klasy ostateczne
◮
użycie słowa kluczowego final przed nazwą klasy uniemożliwia
tworzenia klas pochodnych dziedziczących z tej klasy
◮
wszystkie metody klasy ostatecznej stają się ostateczne – nie
ma możliwości ich przesłonięcia:
final class KlasaPrzykladowa {
int i;
public metoda1() {
i = 1;
}
}
class KlasaPochodna extends KlasaPrzykladowa { } // błąd
Polimorfizm
realizacja w Javie
◮
podobnie jak w innych językach obiektowych polimorfizm jest
realizowany w Javie poprzez mechanizm późnego wiązania
◮
wszelkie wiązania są wiązaniami późnymi z wyjątkiem metod
zadeklarowanych z użyciem słowa kluczowego final oraz metod
prywatnych.
Polimorfizm
rzutowanie w górę
◮
wykorzystanie polimorfizmu jest realizowane poprzez:
mechanizm rzutowania w górę tzn. przypisywania obiektu
utworzonego z klasy pochodnej referencjom klasy bazowej
KlasaBazowa kb = new KlasaPochodna();
◮
wywoływanie metod z klasy bazowej zredefiniowanych w
klasach pochodnych:
kb.metodaNadpisana();
◮
polimorfizm ułatwia rozszerzalność pisanych programów:
możliwe jest wprowadzanie nowych funkcji programu przez
tworzenie nowych klas dziedziczących ze wspólnej klasy
bazowej bez konieczności modyfikowania kodu napisanego dla
klasy bazowej
Polimorfizm
przykład
class Figura {
void rysuj() {}
}
class Okrag extends Figura {
void rysuj() {
System.out.println("Metoda rysuj z Okrag");
}
}
class Prostokat extends Figura {
void rysuj() {
System.out.println("Metoda rysuj z Prostokat");
}
}
class Trojkat extends Figura {
void rysuj() {
System.out.println("Metoda rysuj z Trojkat");
}
}
Polimorfizm
przykład c.d.
public class Figury {
public static Figura wylosujFigure() {
switch((int)(Math.random() * 3)) {
case 0: return new Okrag();
case 1: return new Prostokat();
case 2: return new Trojkat();
}
}
public static void main(String[] args) {
Figura[] f = new Figura[9];
for(int i = 0; i < f.length; i++)
f[i] = wylosujFigure();
for(int i = 0; i < f.length; i++)
f[i].rysuj();
}
}
Polimorfizm
rzutowanie w dół
◮
aby skorzystać z pełnych możliwości obiektu, do którego
dostępne jest odwołanie poprzez referencję do obiektu
nadklasy, konieczne jest wykonanie rzutowania w dół:
Trojkat malyTrojkat = (Trojkat)f[1];
◮
jeżeli podczas takiego odwołania rzutowanie jest zrealizowane
dla niepoprawnego obiektu wygenerowany zostaje wyjątek
◮
aby sprawdzić, czy rzutowanie powiedzie się, można użyć
operatora instanceof:
if (f[1] instanceof Trojkat) {
Trojkat t = (Trojkat)f[1];
}
Klasa abstrakcyjna
◮
klasa abstrakcyjna jest tworzona gdy niezbędne jest tworzenie
klas pochodnych posiadających wspólny interfejs, ale nie jest
wskazane/możliwe definiowanie metod na poziomie klasy
bazowej
◮
klasą abstrakcyjną jest klasa posiadającą metody abstrakcyjne
◮
metodą abstrakcyjną nazywamy metodę posiadającą
deklarację, ale nie posiadającą ciała; metody abstrakcyjne
deklarujemy poprzez użycie słowa kluczowego abstract:
abstract void metoda();
◮
klasa zawierające co najmniej jedną metodę abstrakcyjną musi
zostać zadeklarowana z użyciem słowa kluczowego abstract.
◮
w klasie dziedziczącej z klasy abstrakcyjnej niezbędne jest
zdefiniowanie wszystkich metod abstrakcyjnych; jeżeli tak się
nie stanie klasa ta będzie znowu klasą abstrakcyjną
Interfejsy
◮
interfejs to rozwiązanie wprowadzone w języku Java
wzmacniające pojęcie abstrakcji
◮
utworzenie nowego interfejsu realizowane jest poprzez użycie
słowa kluczowego interface zamiast słowa kluczowego class:
public interface InterfacePrzykladowy {
int metoda1();
int stala1 = 1;
}
◮
w interfejsie możliwe jest:
◮
zadeklarowanie metod (bez możliwości zdefiniowania ich ciał)
◮
zdefiniowanie stałych traktowanych jako statyczne i ostateczne
(public static final).
◮
w interfejsie nie jest możliwe definiowanie atrybutów
Interfejsy
zakres widoczności
◮
definicja interfejsu może być poprzedzona słowem public
(wtedy nazwa interfejsu musi odpowiadać nazwie pliku, w
którym jest on zapisany
◮
bez słowa public - interfejs jest udostępniany w sposób
przyjazny (dostęp pakietowy)
◮
metody dadeklarowane w interfejsie są udostępniane zawsze w
sposób publiczny (nie jest konieczne używanie słowa
kluczowego public przed nazwami metod).
Interfejsy
◮
tworzenie klasy realizującej dany interfejs (lub grupę
interfejsów) odbywa się poprzez użycie słowa kluczowego
implements:
class
KlasaPrzykladowa implements InterfacePrzykladowy
◮
możliwe jest łączenie wielu interfejsów podczas tworzenia klasy
pochodnej. Realizowane jest to poprzez oddzielanie słowa
kluczowego implements przecinkami:
class
KlasaPrzykladowa implements InterfacePrzykladowy, IntefejsInny
◮
możliwe jest używanie dziedziczenie dla interfejsów poprzez
użycie słowa kluczowego extends
◮
dzięki interfejsom możliwe jest uzyskanie pewności, że w danej
klasie zostały udostępnione pewne usługi (zostały
zaimplementowane określone metody)
Interfejsy
... a klasy abstrakcyjne (technicznie)
◮
z technicznego punktu widzenia możliwe byłoby zastępowanie
interfejsów przez klasy abstrakcyjne (i jeden i drugi element
języka operuje na bytach abstrakcyjnych).
◮
praktycznie zastępowanie interfejsów przez klasy abstrakcyjne
nie jest korzystne ze względu na fakt, iż dana klasa może
rozszerzać tylko jedną klasę podstawową. W przypadku
interfejsów nie ma takiego ograniczenia.
◮
interfejsy umożliwiają realizowanie w Javie mechanizmu
wielokrotnego dziedziczenia.
◮
zaleca się wykorzystywanie interfejsów we wszystkich
sytuacjach w których konieczne jest utworzenie klasy bazowej,
ale nie jest konieczne wprowadzanie definicji dla żadnej z
metod
Interfejsy
... a klasy abstrakcyjne (konceptualnie)
◮
◮
◮
Klasy wewnętrzne
◮
klasa wewnętrzna to klasa zdefiniowana wewnątrz innej klasy
◮
powody tworzenia klas wewnętrznych:
◮
obiekt klasy wewnętrznej ma dostęp do zasobów obiektu, który
go stworzył (w tym również do sekcji private)
◮
klasa taka jest ukryta przed innymi klasami w tym samym
pakiecie
◮
klasy tego rodzaju są przydatne przy pisaniu programów
sterujących zdarzeniami
◮
przykład:
class KlasaPrzykladowa {
public KlasaPrzykladowa() { ... }
public void jakasMetoda() { ... }
// Klasa wewnętrzna
private class KlasaWewnetrzna
{
...
}
}