Öröklődés 2

30
Öröklődés 2.

Upload: hoshi

Post on 19-Jan-2016

50 views

Category:

Documents


0 download

DESCRIPTION

Öröklődés 2. Ismétlés – 1-3. példa. //Inicializálatlan változó használata -> FORDÍTÁSI HIBA! Alkalmazott a; a.fizetéstEmel(20); //null referencián keresztül műveletvégzés -> NullPointerException Alkalmazott a = null; a.fizetéstEmel(20000); - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Öröklődés 2

Öröklődés 2.

Page 2: Öröklődés 2

Ismétlés – 1-3. példa

//Inicializálatlan változó használata -> FORDÍTÁSI HIBA!Alkalmazott a;a.fizetéstEmel(20);

//null referencián keresztül műveletvégzés -> NullPointerExceptionAlkalmazott a = null;a.fizetéstEmel(20000);

// objektum példányosításanew Alkalmazott();// objektum példányosítása + változóhoz rendeléseAlkalmazott a = new Alkalmazott();// vagy Alkalmazott a; … a = new Alkalmazott();

Page 3: Öröklődés 2

// referencia állítása objektumra

Alkalmazott a = new Alkalmazott();

Alkalmazott b = a; // Nem készül másolat!!!

b.fizetéstEmel(10000);

System.out.println(a.fizetés); // 10000

Ismétlés – 4. példa

Page 4: Öröklődés 2

class Alkalmazott {

...

// információ elrejtése, invariáns megőrzése

private int fizetés;

// a fizetést kívülről ne lehessen közvetlenül módosítani, mert

// akkor az évesfizetés elromlik!

private int évesFizetés;

// az évesFizetést kívülről sehogyan se lehessen módosítani

// a fizetés megváltozásával szükségszerűen az ő értéke is változzon

public int getFizetés() {

return fizetés;

}

public int getÉvesFizetés() {

return évesFizetés;

}

public void fizetéstBeállít(int új) {

fizetés = új;

évesFizetés = 12*fizetés;

}

}

Ismétlés – 5. példa

Page 5: Öröklődés 2

class Alkalmazott {

...

// osztályszintű változó + metódus

static int nyugdíjKorhatár = 60;

public static void nyugdíjKorhatártEmel( int mennyivel )

{

nyugdíjKorhatár+=mennyivel;

}

}

class Program {

...

// hivatkozás osztályszintű változóra

Alkalmazott a = new Alkalmazott();

System.out.println(a.nyugdíjKorhatár);

// warning -> osztályon keresztül illik elérni

System.out.println(Alkalmazott.nyugdíjKorhatár);

}

Ismétlés – 6. példa

Page 6: Öröklődés 2

metódus szignatúrája = név + paraméterek típusának sorozata túlterheléshez szükségesek a különböző szignatúrák

class Alkalmazott {

...

// túlterhelés

public void fizetéstEmel() {

fizetés += 10000;

}

void fizetéstEmel( int mennyivel ) {

fizetés += mennyivel;

}

}

Ismétlés – 7. példa – túlterhelés

Page 7: Öröklődés 2

class Alkalmazott {

...

// konstruktorok

private int évesFizetés;

public Alkalmazott(String név, int fizetés) {

this.név = név;

this.fizetés = fizetés;

évesFizetés = 12*fizetés;

}

// Mivel írtunk konstruktort, nem jön létre public Alkalmazott() {}

// Másik konstrukor meghívása : this(...)

// Csak a konstruktor első sorában lehet!

public Alkalmazott(String név) {

this(név,100000);

}

}

Ismétlés – 8. példa – konstruktorok

Page 8: Öröklődés 2

Az öröklött adattagokat közvetlenül használhatjuk. Deklarálhatunk azonos nevű adattagot, mint ami már az ősosztályban is

szerepelt. Ezt nevezzük elfedésnek.Adattagok elfedése rossz programozási módszerre vall, használata nem ajánlott.

Deklarálhatunk olyan adattagokat, amelyek az ősosztályban nem szerepelnek.

Az öröklött metódusokat közvetlenül használhatjuk. Írhatunk új példánymetódusokat ugyanolyan névvel és szignatúrával, mint

az ősosztályban. Ezt nevezzük felüldefiniálásnak. (ld. részletesen később) Írhatunk új osztálymetódusokat ugyanolyan névvel és szignatúrával, mint

az ősosztályban. Ezt nevezzük elfedésnek. Deklarálhatunk új metódusokat, amelyek nem szerepelnek az

ősosztályban. Írhatunk konstruktort, ami meghívja az ősosztály valamelyik konstruktorát

(implicit vagy explicit módon).

Öröklődés során mit tehetünk a leszármazott osztályokban?

Page 9: Öröklődés 2

Hozzunk létre egy új csomagot, majd tegyük bele a már megírt Alkalmazott.java osztályt. Hozzuk létre a Főnök osztályt az Alkalmazott osztály leszármazottjaként! Az új adattagok legyenek:

public class Főnök extends Alkalmazott {

private Alkalmazott[] beosztottak;

private static final int maxBeosztott = 10;

private int beosztottakSzáma;

}

Fordítási hibát kapunk! Miért???

Konstruktorhívások a Javában

Page 10: Öröklődés 2

A Főnök osztályban nem írtunk konstruktort, ezért implicit módon létrejön egy üres törzsű.

Ha egy konstruktor nem hív meg másik konstruktort (ez csak az első sorában tehető meg), akkor implicit módon létrejön egy super() hívás az első sorban.

Ez azt jelenti, hogy az Alkalmazott osztály üres konstruktora akar végrehajtódni.

Mivel (az Alkalmazott osztályban írtunk konstruktort, implicit módon nem jön létre üres törzsű) + (nem írtunk üres törzsű konstruktort) = az Alkalmazott osztálynak nincs üres törzsű konstruktora.

Ezek után a fordítási hiba nyilvánvaló.

Konstruktorhívások – a fordítási hiba oka

Page 11: Öröklődés 2

Lehetőleg ne az Alkalmazott osztályban írjunk üres konstruktort! (ezt sok esetben nem is tehetjük meg, hiszen már más által megírt osztályból származtatunk.)

FELADAT: Írjunk a Főnök osztályban egy egy- és egy kétparaméteres

konstruktort! Az egyik hívja meg az Alkalmazott osztály valamelyik

konstruktorát, a másik pedig az egyiket! Gondoljuk át, hogy melyik legyen az egyik, és melyik a másik? Írjunk egy újBeosztott metódust is, amely a paraméterként kapott

alkalmazottat felveszi a beosztottak közé, ha van még hely, és igazzal tér vissza. Ha már nincs hely, akkor térjen vissza hamissal.

Konstruktorhívások – Hogyan javítsuk a hibát?

Page 12: Öröklődés 2

public Főnök(String név, int fizetés) {

super(név,fizetés);

beosztottak = new Alkalmazott[maxBeosztott];

actBeosztott = 0;

}

public Főnök(String név) {

this(név,200000);

}

boolean újBeosztott(Alkalmazott a) {

if (beosztottakSzáma<maxBeosztott) {

beosztottak[beosztottakSzáma] = a;

beosztottakSzáma++;

return true;

}

else {

return false;

}

}

Konstruktorhívások

Page 13: Öröklődés 2

A főrogramban a

Főnök f = new Főnök("Feri");

sor hatására milyen sorrendben hívódnak meg és futnak le a különböző konstruktorok?

1. Meghívódik a Főnök osztály egyparaméteres konstruktora.

2. Mivel az első sorában hívunk meg másik konstruktort, az fog végrehajtódni. Tehát elkezdődik a Főnök osztály kétparaméteres konstruktorának a végrehajtása.

3. A JRE újra megvizsgálja, hogy ez a konstruktor hív-e meg másik konstruktort. A válasz igen, mégpedig az Alkalmazott osztály kétparaméteres konstruktorát. Így ennek a végrehajtása kezdődik meg.

4. Meghívódik az Object osztály paraméter nélküli konstruktora!!! Miért???

5. Befejeződik az Alkalmazott osztály kétparaméteres konstruktorának végrehajtása.

6. Befejeződik a Főnök osztály kétparaméteres konstruktorának végrehajtása.

7. Befejeződik a Főnök osztály egyparaméteres konstruktorának végrehajtása .

Konstruktorhívások – a lefutási sorrend

Page 14: Öröklődés 2

Egy A típus altípusa egy B típusnak, ha minden olyan helyzetben, ahol B típusú objektum használható, A típusú objektum is használható.

Ez a tulajdonság teljesül a Java öröklődése esetén. Miért?

Megjegyzés: A múlt órán csak az öröklődés másik fő előnyét, a kód-újrafelhasználást használtuk ki.

Altípus fogalma (informálisan)

Page 15: Öröklődés 2

Altípusos polimorfizmusnak azt a jelenséget nevezzük, hogy egy rögzített típusú változó (pl. Alkalmazott) hivatkozhat több különböző típusú objektumra (pl. Alkalmazott, Főnök).

Emellett a műveletek is „több típussal rendelkezhetnek”: az Alkalmazott osztály műveletei meghívhatók Főnök típusú objektumokra is.

Polimorfizmus (altípusos polimorfizmus)

Page 16: Öröklődés 2

Az Alkalmazott osztály műveletei meghívhatók Főnök típusú objektumokra is:Főnök f = new Főnök("Feri");

int i = f.getFizetés();

Egy Alkalmazott típusú referenciának értékül adható egy Főnök típusú objektum:Alkalmazott a = new Főnök("Feri");

Egy Alkalmazott formális paraméterrel rendelkező metódus meghívható Főnök típusú aktuális paraméterrel:Főnök f = new Főnök("Feri");

Alkalmazott a = new Alkalmazott("Pisti");

boolean b = a.többetKeresMint(f);

Polimorfizmus

Page 17: Öröklődés 2

Ha egy Alkalmazott típusúként deklarált referenciaváltozó egy Főnök típusú objektumra vonatkozik, akkor mi is az igazi típusa a változónak?

Statikus típus: a deklarációnál megadott típus Dinamikus típus: a változó által mutatott objektum

tényleges típusa A dinamikus típus vagy maga a statikus típus, vagy

egy leszármazottja. (különben fordítási hibát kapunk)

A statikus típus állandó, a dinamikus típus változhat a program futása során.

Statikus és dinamikus típus

Page 18: Öröklődés 2

Alkalmazott a = new Alkalmazott(„Pisti"); //statikus=dinamikus=Alkalmazott

Főnök b = new Főnök("Feri"); //statikus típus=dinamikus típus=Főnök

Alkalmazott c = new Főnök("Zoli"); //statikus=Alkalmazott, dinamikus=Főnök

a = c; //st. típus=Alkalmazott, din. típus=Főnök

a = new Alkalmazott("Tomi"); //statikus típus=dinamikus típus=Alkalmazott

a = b; //st. típus=Alkalmazott, din. típus=Főnök

Object o = new Alkalmazott("Józsi");

o.fizetéstEmel(20000); // fordítási hiba: Object-nek nincs ilyen metódusa

Alkalmazott a = o; // fordítási hiba: o nem feltétlenül Alkalmazott

Statikus és dinamikus típus -- példák

Page 19: Öröklődés 2

A statikus típus dönti el azt, hogy milyen műveletet végezhetők a referenciaváltozón keresztül.

A dinamikus típus dönti el azt, hogy a végrehajtandó metódusnak melyik törzse fusson le.

Ld. dinamikus kötés

Statikus és dinamikus típus szerepe

Page 20: Öröklődés 2

A gyermek osztályban sokszor azt szeretnénk, ha másképp tudnánk bizonyos eseményekre reagálni, mint ahogy a szülő reagál.

Például a múlt órai Autó, Taxi osztályokban a költséget számító metódus és a toString() esetén.

Akkor beszélünk felüldefiniálásról, ha: Az ősosztálybeli és a leszármazott osztálybeli

metódus szignatúrája megegyezik. A visszatérési típus megegyezik. (Java 5.0-tól

kovariánsan változhat) A hozzáférési kategória nem szűkülhet a

leszármazott osztályban. A kiváltható kivételek halmaza nem bővülhet.

Felüldefiniálás

Page 21: Öröklődés 2

A múlt órán tulajdonképpen az öröklődésnek „csak” a kód-újrafelhasználó előnyét használtuk ki, az altípusképző előnyét nem. (Hiszen ha írtunk volna copy-paste technikával egy sima Taxi osztályt öröklődés nélkül, akkor a főprogam ugyanúgy működne.)

Az öröklődés altípusképző előnyét a dinamikus kötés segítségével lehet kihasználni.

A dinamikus kötés bemutatásához szükség van egy, a leszármazott osztályban felüldefiniált metódusra.

Dinamikus kötés

Page 22: Öröklődés 2

FELADAT Definiáljuk felül a pótlék() metódust a

Főnök osztályban! Adja vissza az Alkalmazott-nak

kiszámított pótlékot, és még beosztottanként 20000 forintot!

Definiáljuk felül a toString() metódust is!

Dinamikus kötés

Page 23: Öröklődés 2

Mit csinál a főprogram?Alkalmazott a = new Alkalmazott("Józsi");

Főnök f = new Főnök("Feri");

f.újBeosztott(a);

int i = a.pótlék();

int j = f.pótlék();

a=f;

int k = a.pótlék(); // DINAMIKUS KÖTÉS!int l = f.teljesFizetés();

// DINAMIKUS KÖTÉS A METÓDUS TÖRZSÉN BELÜL IS!

Dinamikus kötés

Page 24: Öröklődés 2

Nagyon fontos, hogy a dinamikus kötéshez elengedhetetlen a felüldefiniált metódus!

Mindig győződjünk meg róla, hogy tényleg felüldefiniáltuk-e a kívánt metódust, mert ha véletlenül túlterheltük, nem kapunk fordítási hibát, de futási időben nem az fog történni, amit várunk!

Dinamikus kötés – megjegyzések

Page 25: Öröklődés 2

int pótlék(int i) { // a Főnök osztályban írjuk meg!

return super.pótlék()+i;

}

Tegyük fel, hogy még nem definiáltuk felül az Alkalmazott osztály pótlék metódusát. Úgy gondolkodhatunk (helytelenül!!!), hogy a Főnök osztályban úgy számoljunk pótlékot, hogy az Alkalmazott szerinti pótlékhoz adjuk hozzá a paraméterként kapott számot. Ez nem felüldefiniálás, hanem túlterhelés!

Alkalmazott a = new Alkalmazott("Józsi");

Főnök f = new Főnök("Feri");

int i = a.pótlék();

int j = f.pótlék();

int l = a.pótlék(10000); // fordítási hiba

int n = f.pótlék(10000); // OK

a=f;

int k = a.pótlék(); // alkalmazott szerinti pótlék

int m = a.pótlék(10000); // fordítási hiba

Dinamikus kötés – helytelen példa

Page 26: Öröklődés 2

Statikus metódusokat nem lehet felüldefiniálni! Ebből következik, hogy dinamikus kötés sincs statikus

metódusoknál! Abban az esetben, amikor egy leszármazott

osztályban egy azonos szignatúrájú és visszatérési értékű metódust írunk, mint ami van az ősosztályban, akkor nem felüldefiniálásról, hanem elfedésről beszélünk.

Ez azt jelenti, hogy statikus típus alapján dől el, hogy melyik törzs hajtódik végre.

Statikus metódusok és az elfedés

Page 27: Öröklődés 2

A leszármazott osztályba tartozó objektum automatikusan konvertálódik ősosztálybeli objektummá. Ezt nevezik alulról felfelé történő konverziónak.

Alkalmazott a = new Főnök(); Ősosztálybeli objektumot csak explicit módon tudunk leszármazott

osztálybeli objektummá konvertálni.

Alkalmazott a = new Főnök();Főnök f = (Főnök)a;

Ezzel az a probléma, hogy fordítási időben nem derül ki, hogy tényleg Főnök-e a konvertálni kívánt Alkalmazott. Ha ez nem teljesül, akkor futási időben ClassCastException váltódik ki. Ha ezt nem akarjuk, akkor ellenőrzést is végezhetünk az instanceof operátor segítségével.

if (a instanceof Főnök) {

Főnök f2 = (Főnök) a;

...

}

Objektumok konverziói

Page 28: Öröklődés 2

Egy vállalatnál alkalmazottak dolgoznak. Közülük néhányan főnökök is. A főnököknek legyenek alkalmazottaik! Legyen olyan sima alkalmazott és főnök is, akinek van nyelvvizsgája!

A vállalat alkalmazottainak adatait tömbben tároljuk! Mi legyen a tömb objektumra mutató referenciaváltozó statikus típusa? (Object, Alkalmazott vagy Főnök?)

Számítsuk ki az összes alkalmazott (ebbe a főnökök is beletartoznak) pótlékokkal együtt számított átlagos teljes fizetését! Emellett számítsuk ki külön a főnökök pótlékokkal együtt számított átlagos teljes fizetését!

Feladat

Page 29: Öröklődés 2

1. Készítsd el a Pont osztályt! Tulajdonságok: x és y koordináta, művelet: eltolás dx és dy értékekkel.

2. Az adattagokat explicit módon inicializáld 0-ra!

3. Készítsd el a Kör osztályt! Tulajdonságok: középpont (Pont) és sugár (double), műveletek: eltol és nagyít. (a középpontot kell eltolni, nagyításnál a sugarat szorozni)

4. A Kör osztályban vezess be egy új attribútumot, a területet tartsuk benne nyilván! Írd meg a sugaratBeállít műveletet! Használd a Math.PI értéket! Az attribútumok legyenek privátok, a műveletek publikusak! Készíts publikus lekérdező műveleteket a sugárhoz és a területhez!

5. A Pont és a Kör osztályokhoz készítsd el a távolság() metódust, mely megadja az objektum távolságát egy, a paraméterként átadott Pont objektumtól! A Kör osztályban definiálj példánymetódust, mely a paraméterként átadott pont objektumot a kör középpontjába állítja: public void középpontba(Pont p)!

6. A Kör osztályhoz írj illeszkedik() műveletet, mely eldönti, hogy a paraméterként megadott Pont objektum illeszkedik-e a körvonalra -- egy adott tűréshatáron belül. A tűréshatár értéke a Kör osztály jellemzője.

7. A Kör osztály sugaratBeállít metódusának formális paramétere legyen ugyanúgy elnevezve, mint a sugár attribútum!

Házi feladat

Page 30: Öröklődés 2

8. Készíts középpontos tükrözést végző műveleteket a Pont és a Kör osztályokban. A műveleteket meg lehessen hívni Pont objektummal is és két koordinátával (cx,cy) is! Valósítsd meg a középpontos tükrözés műveleteket úgy, hogy egymást hívják!

9. Készítsd el a SzínesPont osztályt a Pont osztály leszármazottjaként! Új tulajdonság: szín. Új műveletek: szín beállítása és lekérdezése. A szín attribútum legyen privát!

10. A Pont és a Kör osztályokhoz készíts konstruktorokat! A Pont osztályhoz csak egyet, aminek a koordinátákat lehet átadni. A Kör osztályhoz hármat: az elsőnek a sugarat és egy Pont objektumot (ez lesz a középpont), a másodiknak a sugarat és a középpont koordinátáit lehet átadni, a harmadik legyen paraméter nélküli konstruktor. Melyik Kör konstruktor hívhat meg másikat? Miért nem fordul a SzínesPont osztály? Javítsd!

11. Készítsd el a SzínesKör osztályt a Kör osztály leszármazottjaként! Új műveletei: a szín beállítása és lekérdezése. Ne vezess be új adattagot: a színes körök színét a középpontjuk színe határozza meg, amely nem közönséges Pont, hanem SzínesPont!

12. Készíts public String toString() műveletet a Pont és SzínesPont osztályokhoz, mely adatokat szolgáltat az ilyen objektumokról egy String formájában!

13. A Pont osztályban definiáljunk println metódust, mely kiírja a pontot a szabványos kimenetre! (Ehhez az előző feladatban megírt toString metódust használjuk!) Nézzük meg, hogyan működik az örökölt println metódus a SzínesPont objektumokon!

14. Hozz létre egy Program osztályt! Ebben írj statikus main metódust! Hozz létre egy körökből álló 5 elemű tömb objektumot! A legnagyobb területű kör jellemzőit írasd ki a képernyőre! Lehetőleg ne használj explicit konverziót!

Házi feladat (folytatás)