– Amigo, szereted a bálnákat?

"Bálnák? Nem, soha nem hallottam róluk."

"Olyan, mint egy tehén, csak nagyobb és úszik. Mellesleg a bálnák tehenektől származtak. Uh, vagy legalábbis közös ősük van. Nem számít."

Polimorfizmus és felülbírálás - 1

"Figyelj ide. Az OOP egy másik nagyon hatékony eszközéről szeretnék beszélni: a polimorfizmusról . Négy jellemzője van."

1) A módszer felülbírálása.

Képzeld el, hogy írtál egy „Tehén” osztályt egy játékhoz. Sok tagváltozója és metódusa van. Ennek az osztálynak az objektumai sokféle dolgot tudnak tenni: sétálni, enni, aludni. A tehenek is harangoznak, amikor sétálnak. Tegyük fel, hogy a legapróbb részletekig mindent megvalósított az osztályban.

Polimorfizmus és felülbírálás - 2

Aztán hirtelen az ügyfél azt mondja, hogy a játék egy új szintjét akarja kiadni, ahol minden akció a tengerben történik, és a főszereplő egy bálna.

Elkezdted tervezni a Bálna osztályt, és rájöttél, hogy ez csak egy kicsit különbözik a Cow osztálytól. Mindkét osztály nagyon hasonló logikát használ, és Ön az öröklődés mellett dönt.

A Cow osztály ideálisan alkalmas szülő osztálynak: már minden szükséges változóval és metódussal rendelkezik. Mindössze annyit kell tennie, hogy hozzáadja a bálna úszási képességét. De van egy probléma: a bálnádnak lábai, szarvai és harangja van. Végül is a Cow osztály valósítja meg ezt a funkciót. Mit tudsz csinálni?

Polimorfizmus és felülbírálás - 3

A módszer felülbírálása segít. Ha olyan metódust örökölünk, amely nem pontosan azt csinálja, amire szükségünk van az új osztályunkban, akkor a metódust lecserélhetjük egy másikra.

Polimorfizmus és felülbírálás - 4

Hogyan történik ez? A leszármazott osztályunkban deklaráljuk a módosítani kívánt metódust (ugyanaz a metódus aláírással, mint a szülő osztályban) . Ezután új kódot írunk a metódushoz. Ez az. Mintha nem létezne a szülő osztály régi metódusa.

Így működik:

Kód Leírás
class Cow
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
Itt két osztályt határozunk meg:  Cow és  WhaleWhaleörököl  Cow.

Az  Whale osztály felülírja a  printName();metódust.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Ez a kód a „ tehén vagyok ” feliratot jeleníti meg a képernyőn.
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Ez a kód az „ I'm a bálna ” feliratot jeleníti meg a képernyőn

Miután örökli Cowés felülírja printName, az Whaleosztály valójában a következő adatokkal és metódusokkal rendelkezik:

Kód Leírás
class Whale
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Egyetlen régi módszerről sem tudunk semmit.

– Őszintén szólva, erre számítottam.

2) De ez még nem minden.

"Tegyük fel, hogy az  Cow osztálynak van egy  printAll, metódusa, amely meghívja a másik két metódust. Ekkor a kód így működne:"

A képernyőn megjelenik:
fehér vagyok,
bálna vagyok

Kód Leírás
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
A képernyőn megjelenik:
fehér vagyok,
bálna vagyok

Ne feledje, hogy amikor a Cow osztály printAll () metódusát hívják meg egy Whale objektumon, akkor a Whale printName() metódusa lesz használatos, nem a Cow's.

Nem az a fontos, hogy a metódus melyik osztályba van írva, hanem az objektum típusa (osztálya), amelyen a metódus meghívódik.

"Látom."

"Csak nem statikus módszereket örökölhet és írhat felül. A statikus módszerek nem öröklődnek, ezért nem bírálhatók felül."

Így néz ki a Whale osztály az öröklődés alkalmazása és a metódusok felülírása után:

Kód Leírás
class Whale
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Így néz ki a Whale osztály az öröklődés alkalmazása és a metódus felülírása után. Egyetlen régi módszerről sem tudunk semmit printName.

3) Típusöntés.

Itt van egy még érdekesebb pont. Mivel egy osztály örökli szülőosztályának összes metódusát és adatát, ennek az osztálynak az objektumára a szülő osztály változói (és a szülő szülője stb., egészen az Object osztályig) hivatkozhatnak . Tekintsük ezt a példát:

Kód Leírás
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printColor();
}
A képernyőn megjelenik:
fehér vagyok.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printColor();
}
A képernyőn megjelenik:
fehér vagyok.
public static void main(String[] args)
{
Object o = new Whale();
System.out.println(o.toString());
}
A képernyőn megjelenik:
Whale@da435a.
A toString() metódus az Object osztályból öröklődik.

"Jó cucc. De miért van erre szükséged?"

"Ez egy értékes tulajdonság. Később meg fogod érteni, hogy nagyon-nagyon értékes."

4) Késői kötés (dinamikus feladás).

Így néz ki:

Kód Leírás
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
A képernyőn megjelenik:
Bálna vagyok.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printName();
}
A képernyőn megjelenik:
Bálna vagyok.

Megjegyezzük, hogy nem a változó típusa határozza meg, hogy melyik printName metódust hívjuk meg (a Cow vagy a Whale osztályét), hanem a változó által hivatkozott objektum típusa.

A Cow változó egy Whale objektumra való hivatkozást tárol, és a Whale osztályban meghatározott printName metódus kerül meghívásra.

– Nos, ezt nem a tisztánlátás kedvéért tették hozzá.

"Igen, ez nem olyan nyilvánvaló. Ne feledje ezt a fontos szabályt:"

A változóhoz meghívható metódusok halmazát a változó típusa határozza meg. De hogy melyik konkrét metódust/implementációt hívják meg, azt a változó által hivatkozott objektum típusa/osztálya határozza meg.

"Megpróbálom."

– Állandóan ebbe fog belefutni, így gyorsan megérti, és soha nem felejti el.

5) Típusöntés.

Az öntvény másként működik referenciatípusoknál, azaz osztályoknál, mint a primitív típusoknál. A szélesítő és szűkítő konverziók azonban vonatkoznak a referenciatípusokra is. Tekintsük ezt a példát:

Konverzió szélesítése Leírás
Cow cow = new Whale();

Klasszikus szélesítő átalakítás. Most már csak a Cow osztályban meghatározott metódusokat hívhatja meg a Whale objektumon.

A fordító csak a Cow típus által meghatározott metódusok meghívására engedi meg a cow változó használatát.

Konverzió szűkítése Leírás
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
Klasszikus szűkítő konverzió típusellenőrzéssel . A Cow típusú tehén változó egy bálna objektumra való hivatkozást tárol .
Ellenőrizzük, hogy ez így van-e , majd végrehajtjuk a (kiszélesítő) típuskonverziót. Ezt típusöntésnek is nevezik .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
A referenciatípus szűkítő átalakítása az objektum típusellenőrzése nélkül is elvégezhető.
Ebben az esetben, ha a tehén változó valami másra mutat, mint egy Whale objektumra, kivételt (InvalidClassCastException) dob a rendszer.

6) És most valami finomat. Az eredeti módszer meghívása.

Néha egy örökölt módszer felülbírálásakor nem akarja teljesen lecserélni. Néha csak egy kicsit szeretne hozzátenni.

Ebben az esetben valóban azt szeretné, hogy az új metódus kódja ugyanazt a metódust hívja meg, de az alaposztályon. És Java hadd csináld ezt. Ez így történik:  super.method().

Íme néhány példa:

Kód Leírás
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.print("This is false: ");
super.printName();

System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
A képernyőn megjelenik:
fehér vagyok
Ez hamis: tehén vagyok, bálna
vagyok

"Hmm. Nos, ez egy lecke volt. A robotfülem majdnem elolvadt."

"Igen, ez nem egyszerű dolog. Ez az egyik legnehezebb anyag, amivel találkozni fogsz. A professzor megígérte, hogy linkeket ad más szerzők anyagaihoz, hogy ha még mindig nem értesz valamit, akkor kitöltheted a rések."