Ierarhia moștenirii și conversia tipurilor
În capitolul anterior s-a discutat despre conversia obiectelor de tipuri simple. Cu toate acestea, cu obiectele claselor lucrurile stau puțin diferit. Să presupunem că avem următoarea ierarhie de clase:
public class Program{
public static void main(String[] args) {
Person tom = new Person("Tom");
tom.display();
Person sam = new Employee("Sam", "Oracle");
sam.display();
Person bob = new Client("Bob", "DeutscheBank", 3000);
bob.display();
}
}
// clasa persoană
class Person {
private String name;
public String getName() { return name; }
public Person(String name){
this.name=name;
}
public void display(){
System.out.printf("Person %s \n", name);
}
}
// angajat al unei companii
class Employee extends Person{
private String company;
public Employee(String name, String company) {
super(name);
this.company = company;
}
public String getCompany(){ return company; }
public void display(){
System.out.printf("Employee %s works in %s \n", super.getName(), company);
}
}
// clasa clientului unei bănci
class Client extends Person{
private int sum; // Variabilă pentru a stoca suma din cont
private String bank;
public Client(String name, String bank, int sum) {
super(name);
this.bank=bank;
this.sum=sum;
}
public void display(){
System.out.printf("Client %s has account in %s \n", super.getName(), bank);
}
public String getBank(){ return bank; }
public int getSum(){ return sum; }
}
În această ierarhie de clase putem urmări următorul lanț de moștenire: Object (toate clasele moștenesc implicit de la tipul Object) -> Person -> Employee|Client.

Superclasele sunt de obicei plasate deasupra subclaselor, astfel încât în vârful moștenirii se află clasa Object, iar la bază Employee și Client.
Un obiect al subclasei reprezintă, de asemenea, un obiect al superclasei. De aceea, în program putem scrie astfel:
Object tom = new Person("Tom");
Object sam = new Employee("Sam", "Oracle");
Object kate = new Client("Kate", "DeutscheBank", 2000);
Person bob = new Client("Bob", "DeutscheBank", 3000);
Person alice = new Employee("Alice", "Google");
Aceasta este așa-numita conversie ascendentă (de la subclasă în jos la superclasă în sus în ierarhie) sau upcasting. O astfel de conversie este realizată automat.
Inversul nu este întotdeauna adevărat. De exemplu, un obiect de tip Person nu este întotdeauna un obiect de tip Employee sau Client. De aceea, conversia descendentă sau downcasting de la superclasă la subclasă nu este realizată automat. În acest caz, trebuie să utilizăm operația de conversie a tipurilor.
Object sam = new Employee("Sam", "Oracle");
// conversia descendentă de la Object la tipul Employee
Employee emp = (Employee)sam;
emp.display();
System.out.println(emp.getCompany());
În acest caz, variabila sam este convertită la tipul Employee. Și apoi, prin obiectul emp, putem accesa funcționalitățile obiectului Employee.
Putem converti obiectul Employee pe toată linia dreaptă de moștenire, de la Object la Employee.
Exemple de conversii descendente:
Object kate = new Client("Kate", "DeutscheBank", 2000);
((Person)kate).display();
Object sam = new Employee("Sam", "Oracle");
((Employee)sam).display();
Dar să luăm în considerare o altă situație:
Object kate = new Client("Kate", "DeutscheBank", 2000);
Employee emp = (Employee) kate;
emp.display();
// sau astfel
((Employee)kate).display();
În acest caz, variabila de tip Object conține o referință la un obiect de tip Client. Putem fără erori să convertim acest obiect la tipurile Person sau Client. Dar încercând să îl convertim la tipul Employee, vom obține o eroare la rulare. Acest lucru se întâmplă deoarece kate nu reprezintă un obiect de tip Employee.
Operatorul instanceof
În exemplul de mai sus vedem clar că variabila kate este o referință la un obiect de tip Client, nu Employee. Cu toate acestea, adesea datele vin din exterior și s-ar putea să nu știm exact ce tip de obiect reprezintă aceste date. Astfel, există o probabilitate mare de a întâmpina o eroare. Și înainte de a efectua conversia tipurilor, putem verifica dacă putem face conversia folosind operatorul instanceof:
Object kate = new Client("Kate", "DeutscheBank", 2000);
if(kate instanceof Employee){
Employee employeeKate = (Employee) kate;
employeeKate.display();
}
else{
System.out.println("Conversion is invalid");
}
Expresia kate instanceof Employee verifică dacă variabila kate este un obiect de tip Employee. Dar, deoarece în acest caz evident nu este, această verificare va returna false, iar conversia nu va avea succes.
Expresia kate instanceof Client ar returna true:
Object kate = new Client("Kate", "DeutscheBank", 2000);
if(kate instanceof Client){
Client clientKate = (Client) kate;
clientKate.display();
}
else{
System.out.println("Conversion is invalid");
}
Trebuie menționat că, începând cu versiunea Java 16, putem simplifica conversia tipurilor în felul următor:
Object kate = new Client("Kate", "DeutscheBank", 2000);
if(kate instanceof Client clientKate){
clientKate.display();
}
else{
System.out.println("Conversion is invalid");
}
Expresia:
kate instanceof Client clientKate
Verifică dacă variabila kate reprezintă clasa Client, iar dacă reprezintă (adică operatorul instanceof returnează true), atunci creează o variabilă clientKate de tip Client. Și ulterior putem utiliza această variabilă clientKate și să efectuăm diverse operații cu ea.