„Bună, Amigo! Subiectul lecției de astăzi este lărgirea și restrângerea conversiilor de tip. Ai învățat despre extinderea și îngustarea tipurilor primitive cu mult timp în urmă. La nivelul 10. Astăzi vom vorbi despre cum funcționează pentru tipurile de referință, adică exemple de clase”.

De fapt, totul este destul de simplu. Imaginați-vă lanțul de moștenire al unei clase: clasa, părintele ei, părintele părintelui etc., până la clasa Object. Deoarece o clasă conține toate metodele membre ale clasei pe care o moștenește , o instanță a clasei poate fi salvată într-o variabilă al cărei tip este cel al oricăruia dintre părinții săi.

Iată un exemplu:

Cod Descriere
class Animal
{
public void doAnimalActions();
}class Cat extends Animal
{
public void doCatActions();
}class Tiger extends Cat
{
public void doTigerActions();
}
Aici avem trei declarații de clasă: Animal, Pisică și Tigru. Pisica moștenește animalul. Și Tiger moștenește Cat.
public static void main(String[] args)
{
Tiger tiger = new Tiger();
Cat cat = new Tiger();
Animal animal = new Tiger();
Object obj = new Tiger();
}
Un obiect Tiger poate fi întotdeauna atribuit unei variabile al cărei tip este cel al unuia dintre strămoșii săi. Pentru clasa Tiger, acestea sunt Cat, Animal și Object.

Acum să aruncăm o privire la extinderea și restrângerea conversiilor.

Dacă o operațiune de atribuire ne determină să trecem în sus în lanțul de moștenire (spre clasa Object), atunci avem de-a face cu o conversie de extindere (cunoscută și ca upcasting). Dacă ne deplasăm în jos în lanț spre tipul obiectului, atunci este o conversie de îngustare (cunoscută și sub denumirea de downcasting).

Deplasarea în sus în lanțul de moștenire se numește lărgire, deoarece duce la un tip mai general. Cu toate acestea, făcând acest lucru, pierdem capacitatea de a invoca metodele adăugate la clasă prin moștenire.

Cod Descriere
public static void main(String[] args)
{
Object obj = new Tiger();
Animal animal = (Animal) obj;
Cat cat = (Cat) obj;
Tiger tiger = (Tiger) animal;
Tiger tiger2 = (Tiger) cat;
}
Când restrângeți tipul, trebuie să utilizați un operator de conversie de tip, adică efectuăm o conversie explicită.

Acest lucru face ca mașina Java să verifice dacă obiectul moștenește într-adevăr tipul în care dorim să-l convertim.

Această mică inovație a produs o reducere multiplă a numărului de erori de tip casting și a crescut semnificativ stabilitatea programelor Java.

Cod Descriere
public static void main(String[] args)
{
Object obj = new Tiger();
if (obj instanceof Cat)
{
Cat cat = (Cat) obj;
cat.doCatActions();
}}
Mai bine, folosește o  verificare de instanță
public static void main(String[] args)
{
Animal animal = new Tiger();
doAllAction(animal);

Animal animal2 = new Cat();
doAllAction(animal2);

Animal animal3 = new Animal();
doAllAction(animal3);
}

public static void doAllAction(Animal animal)
{
if (animal instanceof Tiger)
{
Tiger tiger = (Tiger) animal;
tiger.doTigerActions();
}

if (animal instanceof Cat)
{
Cat cat = (Cat) animal;
cat.doCatActions();
}

animal.doAnimalActions();
}
Și iată de ce. Aruncă o privire la exemplul din stânga.

Noi (codul nostru) nu știm întotdeauna cu ce tip de obiect lucrăm. Poate fi un obiect de același tip cu variabila (Animal) sau orice tip descendent (Pisică, Tigru).

Luați în considerare metoda doAllAction. Funcționează corect, indiferent de tipul de obiect transmis.

Cu alte cuvinte, funcționează corect pentru toate cele trei tipuri: Animal, Pisică și Tigru.

public static void main(String[] args)
{
Cat cat = new Tiger();
Animal animal = cat;
Object obj = cat;
}
Aici avem trei operațiuni de atribuire. Toate acestea sunt exemple de extindere a conversiilor.

Operatorul de tip turnare nu este necesar aici, deoarece nu este necesară nicio verificare. O referință la obiect poate fi întotdeauna stocată într-o variabilă al cărei tip este unul dintre strămoșii săi.

„Oh, al doilea exemplu a arătat totul clar: de ce este nevoie de verificare și de ce este necesară turnarea tipului.”

"Sper. Vreau să vă atrag atenția asupra acestui fapt:"

Nimic din toate acestea nu face ca un obiect să se schimbe în vreun fel! Singurul lucru care se schimbă este numărul de metode disponibile pentru a fi apelate pe o anumită variabilă de referință.

De exemplu, o variabilă Cat vă permite să apelați metodele doAnimalActions și doCatActions. Nu știe nimic despre metoda doTigerActions, chiar dacă indică un obiect Tiger.

"Da, am înțeles. A fost mai ușor decât credeam că va fi."