Иерархия наследования и преобразование типов

Последнее обновление: 24.09.2025

В прошлой главе говорилось о преобразованиях объектов простых типов. Однако с объектами классов все происходит немного по-другому. Допустим, у нас есть следующая иерархия классов:

public class Program{
      
    public static void main(String[] args) {
             
        Person tom = new Person("Tom");
        tom.print();        // Person Tom
        Person sam = new Employee("Sam", "Yandex");
        sam.print();        // Employee Sam works in Yandex
        Person bob = new Client("Bob", "SberBank");
        bob.print();    // Client Bob has account in SberBank
    }
}
// класс человека
class Person {
     
    private String name;
    String getName() {return name;}
    
    Person(String name){
     
        this.name=name;
    }
  
    void print(){
         
        System.out.printf("Person %s \n", name);
    }
}
// служащий некоторой компании 
class Employee extends Person{
 
    private String company;
    String getCompany(){return company;}
     
    Employee(String name, String company) {
     
        super(name);
        this.company = company;
    }
     
    void print(){
         
        System.out.printf("Employee %s works in %s \n", getName(), company);
    }
}
// класс клиента банка
class Client extends Person{
       
    private String bank;
    String getBank(){return bank;}
       
    Client(String name, String bank) {
       
        super(name);
        this.bank=bank;
    }
       
    void print(){
           
        System.out.printf("Client %s has account in %s \n", getName(), bank);
    }
}

В этой иерархии классов можно проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Person -> Employee|Client.

Преобразование типов в языке Java

Суперклассы обычно размещаются выше подклассов, поэтому на вершине наследования находится класс Object, а в самом низу Employee и Client.

upcasting или восходящие преобразования

Объект подкласса также представляет объект суперкласса. Поэтому в программе мы можем написать следующим образом:

public class Program{
      
    public static void main(String[] args) {
             
        Object tom = new Person("Tom");                     // преобразование от Person к Object
        Object sam = new Employee("Sam", "Yandex");         // от Employee к Object
        Object kate = new Client("Kate", "SberBank");       // от Client к Object
        Person bob = new Client("Bob", "VTB");              // от Client к Person
        Person alice = new Employee("Alice", "VK");         // от Employee к Person
    }
}

Это так называемое восходящее преобразование (от подкласса внизу к суперклассу вверху иерархии) или upcasting. Такое преобразование осуществляется автоматически.

downcasting или нисходящее преобразование

Обратное не всегда верно. Например, объект Person не всегда является объектом Employee или Client. Поэтому нисходящее преобразование или downcasting от суперкласса к подклассу автоматически не выполняется. В этом случае нам надо использовать операцию преобразования типов. Например:

Object sam = new Employee("Sam", "Yandex");
sam.print(); // ! Ошибка

Здесь хотя переменная sam и хранит ссылку на объект типа Employee, но является переменной типа Object. Поэтому при компиляции мы столкнемся с ошибкой - поскольку объект типа Object не обязательно представляет тип Employee или Person, у него может и не быть метода print(). И в этом случае нам надо применить нисходящее преобразование от типа Object к типу Employee или Person:

Object sam = new Employee("Sam", "Yandex");

// нисходящее преобразование от Object к типу Employee
Employee emp = (Employee)sam;
emp.print();
System.out.println(emp.getCompany());

В данном случае переменная sam приводится к типу Employee. И затем через объект emp мы можем обратиться к функционалу объекта Employee.

Мы можем преобразовать объект Employee по всей прямой линии наследования от Object к Employee.

Примеры нисходящих перобразований:

public class Program{
      
    public static void main(String[] args) {
             
        Object kate = new Client("Kate", "Sberbank");
        ((Person)kate).print();
                
        Object sam = new Employee("Sam", "Yandex");
        ((Employee)sam).print();
    }
}

Но рассмотрим еще одну ситуацию:

public class Program{
      
    public static void main(String[] args) {
             
        Person kate = new Client("Kate", "Sberbank");
        Employee emp = (Employee) kate;   // ! Ошибка - java.lang.ClassCastException
        emp.print();

        // или так
        ((Employee)kate).print();
    }
}

В данном случае переменная kate, которая имеет тип Person, хранит ссылку на объект Client. Мы можем без ошибок привести этот объект к типу Client. Но при попытке преобразования к типу Employee мы получим ошибку во время выполнения. Так как kate НЕ представляет объект типа Employee.

Оператор instanceof

В примере выше мы явно видим, что переменная kate - это ссылка на объект Client, а не Employee. Однако нередко данные приходят извне, и мы можем точно не знать, какой именно объект эти данные представляют. Соответственно возникает большая вероятность столкнуться с ошибкой. И перед тем, как провести преобразование типов, мы можем проверить, а можем ли мы выполнить приведение с помощью оператора instanceof:

public class Program{
      
    public static void main(String[] args) {
             
        Person kate = new Client("Kate", "Sberbank");
        printPerson(kate);  // Person is not an employee

        Person sam = new Employee("Sam", "Yandex");
        printPerson(sam);  // Employee works in Yandex
    }

    static void printPerson(Person person){
        if(person instanceof Employee){
     
            Employee empl = (Employee) person;
            System.out.println("Employee works in " + empl.getCompany());
        }
        else{
                            
            System.out.println("Person is not an employee");
        }
    }
}

В методе printPerson выражение person instanceof Employee проверяет, представляет ли параметр person объект типа Employee. И если не является, то такая проверка вернет значение false, и преобразование не сработает, и выполняется блок else:

else{
                            
    System.out.println("Person is not an employee");
}

Если же параметр person представляет тип Employee, то выполняется блко if, где преобразуем в тип Employee и выводим на консоль информацию об объекте:

if(person instanceof Employee){
     
    Employee empl = (Employee) person;
    System.out.println("Employee works in " + empl.getCompany());
}
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.