В прошлой главе говорилось о преобразованиях объектов простых типов. Однако с объектами классов все происходит немного по-другому. Допустим, у нас есть следующая иерархия классов:
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(); } } // класс человека 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); } } // служащий некоторой компании 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); } } // класс клиента банка class Client extends Person{ private int sum; // Переменная для хранения суммы на счете 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; } }
В этой иерархии классов можно проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Person -> Employee|Client.
Суперклассы обычно размещаются выше подклассов, поэтому на вершине наследования находится класс Object, а в самом низу Employee и Client.
Объект подкласса также представляет объект суперкласса. Поэтому в программе мы можем написать следующим образом:
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");
Это так называемое восходящее преобразование (от подкласса внизу к суперклассу вверху иерархии) или upcasting. Такое преобразование осуществляется автоматически.
Обратное не всегда верно. Например, объект Person не всегда является объектом Employee или Client. Поэтому нисходящее преобразование или downcasting от суперкласса к подклассу автоматически не выполняется. В этом случае нам надо использовать операцию преобразования типов.
Object sam = new Employee("Sam", "Oracle"); // нисходящее преобразование от Object к типу Employee Employee emp = (Employee)sam; emp.display(); System.out.println(emp.getCompany());
В данном случае переменная sam приводится к типу Employee. И затем через объект emp мы можем обратиться к функционалу объекта Employee.
Мы можем преобразовать объект Employee по всей прямой линии наследования от Object к Employee.
Примеры нисходящих перобразований:
Object kate = new Client("Kate", "DeutscheBank", 2000); ((Person)kate).display(); Object sam = new Employee("Sam", "Oracle"); ((Employee)sam).display();
Но рассмотрим еще одну ситуацию:
Object kate = new Client("Kate", "DeutscheBank", 2000); Employee emp = (Employee) kate; emp.display(); // или так ((Employee)kate).display();
В данном случае переменная типа Object хранит ссылку на объект Client. Мы можем без ошибок привести этот объект к типам Person или Client. Но при попытке преобразования к типу Employee мы получим ошибку во время выполнения. Так как kate не представляет объект типа Employee.
В примере выше мы явно видим, что переменная kate - это ссылка на объект Client, а не Employee. Однако нередко данные приходят извне, и мы можем точно не знать, какой именно объект эти данные представляют. Соответственно возникает большая вероятность столкнуться с ошибкой. И перед тем, как провести преобразование типов, мы можем проверить, а можем ли мы выполнить приведение с помощью оператора 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"); }
Выражение kate instanceof Employee
проверяет, является ли переменная kate объектом типа Employee. Но так как в данном случае явно
не является, то такая проверка вернет значение false
, и преобразование не сработает.
А выражение kate instanceof Client
возвратило бы 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"); }
Следует отметить, что начиная с версии Java 16 мы можем упростить преобразование типов следующим образом:
Object kate = new Client("Kate", "DeutscheBank", 2000); if(kate instanceof Client clientKate){ clientKate.display(); } else{ System.out.println("Conversion is invalid"); }
Выражение
kate instanceof Client clientKate
Проверяет, представляет ли переменная kate
класс Client
, и если представляет (то есть оператор instanceof
возвращает true
), то создает переменную clientKate
типа Client
. И в дальнейшем мы можем использовать эту переменную clientKate и производить
с ней различные операции.