Доброго часу, джаварашовець. Почали мені надходити питання про приведення типів посилань в Java. Що б щоразу не переказувати те саме, вирішив написати маленьку статтю.
Спочатку розберемо, що таке приведення типів
Приведення типів (перетворення типів) — перетворення значення змінної одного типу на значення іншого типу. Погляньмо на прикладі, що це таке і з чим його їдять. У нас є деяка ієрархія класів ( див. рисунок 1 ). Тут видно всі класи ієрархії, хто успадковує і методи кожного класу. Є розширювальне та звужувальне приведення. Ми бачимо, що класCat
є спадкоємцем класу Pet
. Pet
, своєю чергою, спадкоємець класу Animal
. Коли ми напишемо:
Animal animalCat = new Cat();
Animal animalDog = new YorkshireTerrier();
Це розширююче приведення (чи неявне). Ми розширабо посилання animalCat
та animalDog
. Вони посилаються на об'єкти Cat
та Dog
. При такому наведенні ми не можемо через посилання animalCat/animalDog
викликати методи, які є в Cat/Dog
, але яких немає в Animal
. Звуження приведення (або явне) відбувається у зворотний бік:
Animal animalCat = new Cat();
Animal animalDog = new YorkshireTerrier();
Cat cat =(Cat)animalCat;
YorkshireTerrier dog = (YorkshireTerrier) animalDog;
Ми явно вказали до якого типу хочемо навести цей об'єкт. BUT, BE CAREFUL! Якщо Ви зробите так:
Animal animalCat = new Cat();
YorkshireTerrier dog = (YorkshireTerrier) animalCat;
компілятор пропустить цей код. А ось RunTime
викине вам:
Exception in thread "main" java.lang.ClassCastException: Animals.Cat cannot be cast to Animals.YorkshireTerrier
RunTime
бачить, що Cat
й YorkshireTerrier
два різні класи. Щоб уникнути ClassCastException при звужуванні, використовуйте instanceof
.
Animal animalCat = new Cat();
if (animalCat instanceof YorkshireTerrier)
{
YorkshireTerrier dog = (YorkshireTerrier) animalCat;
}
Якщо animalCat
є YorkshireTerrier
, тоді відбудеться присвоєння, якщо ні – нічого не станеться.
А тепер навіщо це потрібно, якщо ми втрачаємо методи і можемо отримати такі помилки
Розглянемо код, який я зробив за діаграмою з Мал. 1 . КласAnimal
public abstract class Animal
{
String name;
int age;
String nameOfClass = getClass().getSimpleName();
public void eat(){
System.out.println(nameOfClass + ": Omnomnom");
}
public void sleep(){
System.out.println(nameOfClass + ": Z-z-z-z");
}
}
Клас WildAnimal
який успадковується відAnimal
public abstract class WildAnimal extends Animal
{
public void steelChicken()
{
System.out.println(nameOfClass+": Muhaha,I stole a chicken!");
}
}
Клас Pet
який успадковується відAnimal
public abstract class Pet extends Animal
{
public void peeInTray(){
System.out.println(nameOfClass + ": Master, I peed");
}
}
Клас Fox
який успадковується відWildAnimal
public class Fox extends WildAnimal
{
public void eatColobok(){
System.out.println(nameOfClass + ": I will eat you, Colobok");
}
}
Клас Wolf
який успадковується відWildAnimal
public class Wolf extends WildAnimal
{
public void hawlAtTheMoon(){
System.out.println(nameOfClass + ": Ouuuuu!!!Ouuuu!!!");
}
}
Клас Cat
який успадковується відPet
public class Cat extends Pet
{
public void sleepOnKeyboard(){
System.out.println(nameOfClass + ": Master, stop working!!I wanna sleep on your keyboard");
}
}
Клас YorkshireTerrier
який успадковується відPet
public class YorkshireTerrier extends Pet
{
public void bark(){
System.out.println(nameOfClass + ": Meow!!! Meow!!!");
}
}
Уявіть собі ситуацію. Нам потрібно зібрати всіх тварин в один список, нагодувати їх і потім укласти спати. Це легко зробити, якщо ми створимо ArrayList
тварин ( Animal
). І потім у кожної тварини викличемо відповідні методи:
public class ZOO
{
public static void main(String[] args)
{
List<Animal> allAnimals = new ArrayList<>();
allAnimals.add(new Cat());
allAnimals.add(new Wolf());
allAnimals.add(new Fox());
allAnimals.add(new YorkshireTerrier());
for (Animal animal : allAnimals)
{
animal.eat();
animal.sleep();
}
}
}
Я не можу animal
викликати метод bark()
або sleepOnKeyboard()
. Тому що в листі allAnimals
зберігаються кіт, вовк, йорік та лисиця, але вони приведені до Animal
. І вони мають лише ті методи, які є в Animal
. Це дуже добре, тому що, якби ми могли викликати всі методи, то навіщо нам потрібен вовк, який спить на клавіатурі, чи йорік, який краде курок? Дякую за увагу. Сподіваюся, ця стаття для Вас буде корисною. Критика та коментарі вітаються)