„Amigo, îți plac balenele?”

"Balene? Nu, nu am auzit de ele."

"Este ca o vaca, doar mai mare și înoată. De altfel, balenele au venit de la vaci. A, sau cel puțin au un strămoș comun. Nu contează."

Polimorfism și suprascriere - 1

"Ascultă. Vreau să vă spun despre un alt instrument foarte puternic al POO: polimorfismul . Are patru caracteristici."

1) Anularea metodei.

Imaginează-ți că ai scris o clasă „Vaca” pentru un joc. Are o mulțime de variabile și metode membre. Obiectele acestei clase pot face diverse lucruri: să meargă, să mănânce, să doarmă. De asemenea, vacile sună un clopoțel când merg. Să presupunem că ați implementat totul în clasă până la cel mai mic detaliu.

Polimorfism și suprascriere - 2

Apoi, dintr-o dată, clientul spune că vrea să lanseze un nou nivel al jocului, în care toate acțiunile au loc în mare, iar personajul principal este o balenă.

Ați început să proiectați clasa Whale și vă dați seama că este doar puțin diferită de clasa Cow. Ambele clase folosesc o logică foarte similară și decideți să utilizați moștenirea.

Clasa Cow este ideală pentru a fi clasa părinte: are deja toate variabilele și metodele necesare. Tot ce trebuie să faceți este să adăugați capacitatea de a înota a balenei. Dar există o problemă: balena ta are picioare, coarne și un clopot. La urma urmei, clasa Cow implementează această funcționalitate. Ce poti face?

Polimorfism și suprascriere - 3

Depășirea metodei vine în ajutor. Dacă moștenim o metodă care nu face exact ceea ce avem nevoie în noua noastră clasă, putem înlocui metoda cu alta.

Polimorfism și suprascriere - 4

Cum se face asta? În clasa noastră descendentă, declarăm metoda pe care vrem să o schimbăm (cu aceeași semnătură a metodei ca în clasa părinte) . Apoi scriem cod nou pentru metodă. Asta este. Este ca și cum vechea metodă a clasei părinte nu ar exista.

Iată cum funcționează:

Cod Descriere
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");
}
}
Aici definim două clase:  Cow și  WhaleWhalemoștenește  Cow.

Clasa  Whale suprascrie  printName();metoda.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Acest cod afișează „ Sunt o vacă ” pe ecran.
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Acest cod afișează „ Sunt o balenă ” pe ecran

După ce moștenește Cowși înlocuiește printName, Whaleclasa are de fapt următoarele date și metode:

Cod Descriere
class Whale
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Nu știm nimic despre vreo metodă veche.

— Sincer, la asta mă așteptam.

2) Dar asta nu este tot.

„Să presupunem că  Cow clasa are o  printAllmetodă , care apelează celelalte două metode. Atunci codul ar funcționa astfel:”

Ecranul va afișa:
Sunt alb,
sunt o balenă

Cod Descriere
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();
}
Ecranul va afișa:
Sunt alb,
sunt o balenă

Rețineți că atunci când metoda printAll () a clasei Cow este apelată pe un obiect Whale, va fi folosită metoda printName() a lui Whale, nu a lui Cow.

Important nu este clasa în care este scrisă metoda, ci mai degrabă tipul (clasa) obiectului pe care este apelată metoda.

"Înțeleg."

„Puteți moșteni și înlocui doar metodele non-statice. Metodele statice nu sunt moștenite și, prin urmare, nu pot fi suprascrise.”

Iată cum arată clasa Whale după ce aplicăm moștenirea și înlocuim metodele:

Cod Descriere
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");
}
}
Iată cum arată clasa Whale după ce aplicăm moștenirea și înlocuim metoda. Nu știm nimic despre vreo printNamemetodă veche.

3) Tip turnare.

Iată un punct și mai interesant. Deoarece o clasă moștenește toate metodele și datele clasei sale părinte, un obiect al acestei clase poate fi referit de variabilele clasei părinte ( și părintele părintelui etc., până la clasa Object). Luați în considerare acest exemplu:

Cod Descriere
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printColor();
}
Ecranul va afișa:
Sunt alb.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printColor();
}
Ecranul va afișa:
Sunt alb.
public static void main(String[] args)
{
Object o = new Whale();
System.out.println(o.toString());
}
Ecranul va afișa:
Whale@da435a.
Metoda toString() este moștenită din clasa Object.

"Lucruri bune. Dar de ce ai avea nevoie de asta?"

"Este o caracteristică valoroasă. Veți înțelege mai târziu că este foarte, foarte valoroasă."

4) Legare tardivă (dispecerare dinamică).

Iată cum arată:

Cod Descriere
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Ecranul va afișa:
Sunt o balenă.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printName();
}
Ecranul va afișa:
Sunt o balenă.

Rețineți că nu tipul variabilei determină ce metodă specifică printName numim (cea a clasei Cow sau Whale), ci mai degrabă tipul de obiect referit de variabilă.

Variabila Cow stochează o referință la un obiect Whale , iar metoda printName definită în clasa Whale va fi apelată.

— Ei bine, nu au adăugat asta de dragul clarității.

"Da, nu este chiar atât de evident. Amintește-ți această regulă importantă:"

Setul de metode pe care le puteți apela la o variabilă este determinat de tipul variabilei. Dar ce metodă/implementare specifică este apelată este determinată de tipul/clasa obiectului referit de variabilă.

"Voi încerca."

„Veți întâlni asta în mod constant, așa că o veți înțelege rapid și nu uitați niciodată”.

5) Tip turnare.

Casting funcționează diferit pentru tipurile de referință, adică clase, decât pentru tipurile primitive. Cu toate acestea, conversiile de extindere și restrângere se aplică și tipurilor de referință. Luați în considerare acest exemplu:

Lărgirea conversiei Descriere
Cow cow = new Whale();

O conversie clasică de lărgire. Acum puteți apela doar metode definite în clasa Cow pe obiectul Whale.

Compilatorul vă va permite să utilizați variabila cow doar pentru a apela acele metode definite de tipul Cow.

Îngustarea conversiei Descriere
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
O conversie clasică de îngustare cu verificare de tip. Variabila cow de tip Cow stochează o referință la un obiect Whale.
Verificăm dacă acesta este cazul și apoi efectuăm conversia de tip (lărgire). Aceasta se mai numește și turnare tip .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
De asemenea, puteți efectua o conversie de restrângere a unui tip de referință fără a verifica tipul obiectului.
În acest caz, dacă variabila vacă indică spre altceva decât un obiect Whale, va fi aruncată o excepție (InvalidClassCastException).

6) Și acum pentru ceva gustos. Apelarea metodei originale.

Uneori, când suprascrieți o metodă moștenită, nu doriți să o înlocuiți în întregime. Uneori vrei doar să adaugi puțin la el.

În acest caz, doriți cu adevărat ca codul noii metode să apeleze aceeași metodă, dar pe clasa de bază. Și Java hai să faci asta. Asa se face:  super.method().

Aici sunt cateva exemple:

Cod Descriere
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();
}
Ecranul va afișa:
Sunt alb
Acest lucru este fals: Sunt o vacă
Sunt o balenă

"Hmm. Ei bine, asta a fost o lecție. Urechile mele de robot aproape s-au topit."

"Da, acestea nu sunt lucruri simple. Este unul dintre cele mai dificile materiale pe care le veți întâlni. Profesorul a promis că va oferi link-uri către materiale de la alți autori, astfel încât dacă tot nu înțelegeți ceva, puteți completa goluri.”