Az előadás letöltése folymat van. Kérjük, várjon

Az előadás letöltése folymat van. Kérjük, várjon

Öröklődés 2..

Hasonló előadás


Az előadások a következő témára: "Öröklődés 2.."— Előadás másolata:

1 Öröklődés 2.

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); // objektum példányosítása new Alkalmazott(); // objektum példányosítása + változóhoz rendelése Alkalmazott a = new Alkalmazott(); // vagy Alkalmazott a; … a = new Alkalmazott();

3 Ismétlés – 4. példa // 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

4 Ismétlés – 5. példa 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;

5 Ismétlés – 6. példa 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);

6 Ismétlés – 7. példa – túlterhelés
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;

7 Ismétlés – 8. példa – konstruktorok
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);

8 Öröklődés során mit tehetünk a leszármazott osztályokban?
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).

9 Konstruktorhívások a Javában
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???

10 Konstruktorhívások – a fordítási hiba oka
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ó.

11 Konstruktorhívások – Hogyan javítsuk a hibát?
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.

12 Konstruktorhívások 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;

13 Konstruktorhívások – a lefutási sorrend
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? Meghívódik a Főnök osztály egyparaméteres konstruktora. 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. 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. Meghívódik az Object osztály paraméter nélküli konstruktora!!! Miért??? Befejeződik az Alkalmazott osztály kétparaméteres konstruktorának végrehajtása. Befejeződik a Főnök osztály kétparaméteres konstruktorának végrehajtása. Befejeződik a Főnök osztály egyparaméteres konstruktorának végrehajtása .

14 Altípus fogalma (informálisan)
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.

15 Polimorfizmus (altípusos polimorfizmus)
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.

16 Polimorfizmus 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: Alkalmazott a = new Alkalmazott("Pisti"); boolean b = a.többetKeresMint(f);

17 Statikus és dinamikus típus
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.

18 Statikus és dinamikus típus -- példák
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

19 Statikus és dinamikus típus szerepe
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

20 Felüldefiniálás 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.

21 Dinamikus kötés 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.

22 Dinamikus kötés 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 forintot! Definiáljuk felül a toString() metódust is!

23 Dinamikus kötés 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!

24 Dinamikus kötés – megjegyzések
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!

25 Dinamikus kötés – helytelen példa
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

26 Statikus metódusok és az elfedés
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.

27 Objektumok konverziói
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; ... }

28 Feladat 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!

29 Házi feladat Készítsd el a Pont osztályt! Tulajdonságok: x és y koordináta, művelet: eltolás dx és dy értékekkel. Az adattagokat explicit módon inicializáld 0-ra! 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) 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! 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)! 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. 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!

30 Házi feladat (folytatás)
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! 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! 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! 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! 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! 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! 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!


Letölteni ppt "Öröklődés 2.."

Hasonló előadás


Google Hirdetések