JavaRush /Java блог /Random UA /Оператор Instanceof в Java

Оператор Instanceof в Java

Стаття з групи Random UA
Вітання! Сьогодні ми поговоримо про оператора instanceof, розглянемо приклади його використання і торкнемося деякі пов'язані з його роботою моменти :) На ранніх рівнях JavaRush ти вже стикався з цим оператором. Пам'ятаєш, навіщо він потрібний? Якщо ні – не біда, давай згадаємо разом. Оператор stanceof необхідний, щоб перевірити, чи об'єкт, на який посилається змінна X, створений на основі будь-якого класу Y. Звучить просто. Чому ми повернулися до цієї теми? Насамперед тому, що тепер ти добре знайомий з механізмом успадкування в Java та іншими принципами ООП. Тема часуподії буде набагато зрозумілішою, і ми розглянемо більш просунуті приклади використання. Поїхали! Як працює оператор Instanceof - 1Ти напевно пам'ятаєш, що оператор instanceof повертає значення trueякщо перевірка показала істинність, або false , якщо результат був хибним. Отже, найчастіше він зустрічається у різноманітних перевірочних умовах ( if…else). Почнемо з прикладів простіше:
public class Main {

   public static void main(String[] args) {

       Integer x = new Integer(22);

       System.out.println(x instanceof Integer);
   }
}
Як гадаєш, що буде виведено в консоль? Ну, тут очевидно :) Об'єкт хє Integer, тому результатом буде true . Висновок у консоль: true Спробуємо перевірити його на приналежність, наприклад, до String:
public class Main {

   public static void main(String[] args) {

       Integer x = new Integer(22);

       System.out.println(x instanceof String);// Помилка!
   }
}
Ми отримали помилку. Причому зверніть увагу: компілятор видав її ще до виконання коду! Він відразу побачив, що Integer і String не можуть бути автоматично перетворені один до одного і не перебувають у зв'язках успадкування. Отже, об'єкт класу Integer не створиться з урахуванням String. Це зручно і допомагає уникнути дивних помилок вже під час виконання програми, тому тут компілятор нас виручив :) Тепер давай спробуємо розглянути приклади складніше. Раз ми згадали успадкування, попрацюємо ось з такою невеликою системою класів:
public class Animal {

}

public class Cat extends Animal {

}

public class MaineCoon extends Cat {

}
Ми вже знаємо, як поводиться instanceof, коли ми перевіряємо приналежність об'єкта до якогось класу у звичайній ситуації, але що буде, якщо ми додамо сюди відношення «батько-нащадок»? Як працює оператор Instanceof - 2 Наприклад, як вважаєш, що видасть ось така перевірка:
public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();

       System.out.println(cat instanceof Animal);

       System.out.println(cat instanceof MaineCoon);

   }
}
Висновок: true false Головне питання, на яке потрібно відповісти, - як саме instanceof розшифровує поняття «об'єкт створений на основі класу»? У нас в результаті вийшло Сat instanceof Animal == true, але ж до такого формулювання можна і причепитися. Чому цей об'єкт Catстворюється на основі класу Animal? Хіба вона не створюється тільки на основі власного класу? Відповідь досить проста, і ти, можливо, вже додумався до неї. Згадай порядок виклику конструкторів та ініціалізації змінних під час створення об'єкта. Ми вже розглядали цю тему у статті про конструктора класів . Ось приклад із тієї лекції:
public class Animal {

   String brain = "Спочаткове значення brain у класі Animal";
   String heart = "Споконвічне значення heart у класі Animal";

   public static int animalCount = 7700000;

   public Animal(String brain, String heart) {
       System.out.println("Виконується конструктор базового класу Animal");
       System.out.println("Чи були вже проініціалізовані змінні класу Animal?");
       System.out.println("Поточне значення статичної змінної animalCount = " + animalCount);
       System.out.println("Поточне значення brain у класі Animal = " + this.brain);
       System.out.println("Поточне значення heart у класі Animal = " + this.heart);

       this.brain = brain;
       this.heart = heart;
       System.out.println("Конструктор базового класу Animal завершив роботу!");
       System.out.println("Поточне значення brain = " + this.brain);
       System.out.println("Поточне значення heart = " + this.heart);
   }
}

class Cat extends Animal {

   String tail = "Споконвічне значення tail у класі Cat";

   static int catsCount = 37;

   public Cat(String brain, String heart, String tail) {
       super(brain, heart);
       System.out.println("Конструктор класу Cat розпочав роботу (конструктор Animal вже був виконаний)");
       System.out.println("Поточне значення статичної змінної catsCount = " + catsCount);
       System.out.println("Поточне значення tail = " + this.tail);
       this.tail = tail;
       System.out.println("Поточне значення tail = " + this.tail);
   }

   public static void main(String[] args) {
       Cat cat = new Cat("Мозок", "Серце", "Хвіст");
   }
}
І якщо ти його запустиш в IDE, висновок у консоль виглядатиме так: Виконується конструктор базового класу Animal Чи були вже проініціалізовані змінні класу Animal? Поточне значення статичної змінної animalCount = 7700000 Поточне значення brain у класі Animal = Початкове значення brain у класі Animal Поточне значення heart у класі Animal = Початкове значення heart у класі Animal Конструктор базового класу Animal завершив роботу! Поточне значення brain = Мозок Поточне значення heart = Серце Конструктор класу Cat почав роботу (конструктор Animal вже був виконаний) Поточне значення статичної змінної catsCount = 37 Поточне значення tail = Початкове значення tail у класі Cat Поточне значення tail = Хвіст Тепер згадав? :) Конструктор базового класу, якщо він є, завжди викликається першим під час створення будь-якого об'єкта. Устанціз керується саме цим принципом, коли намагається визначити, чи був об'єкт Астворений на основі класу Б. Якщо конструктор базового класу викликаний, значить жодних сумнівів не може бути. З другою перевіркою все простіше:
System.out.println(cat instanceof MaineCoon);
Конструктор MaineCoonне викликався під час створення Cat, що логічно. Адже MaineCoonнащадок Cat, а не предок. Але шаблоном Catвін не є. Ок, з цим начебто зрозуміло. А що буде, якщо ми зробимо так:
public class Main {

   public static void main(String[] args) {

       Cat cat = new MaineCoon();

       System.out.println(cat instanceof Cat);
       System.out.println(cat instanceof MaineCoon);


   }
}
Хм...тут уже складніше. Давай спробуємо поміркувати. У нас є змінна типу Cat, і їй ми надали об'єкт типу MaineCoon. До речі, а чому це взагалі працює? Хіба ж так можна робити? Можна, можливо. Адже будь-який мейн-кун – це кішка. Якщо не зовсім зрозуміло, згадай приклад із розширенням примітивних типів:
public class Main {

   public static void main(String[] args) {

       long x = 1024;

   }
}
Число 1024 - це short : він легко поміщається в змінну long , адже за кількістю байт для нього достатньо місця (пам'ятаєш приклад з матрьошками?). Об'єкт-нащадок завжди можна присвоїти у змінну-предка. Поки що просто запам'ятай це, а в наступних лекціях ми ще розберемо цей процес. То що ж виведе наш приклад?
Cat cat = new MaineCoon();
System.out.println(cat instanceof Cat);
System.out.println(cat instanceof MaineCoon);
Що буде перевіряти настанівід: наші змінні класу Catабо наш об'єкт класу MaineCoon? Насправді відповісти на це питання просто. Потрібно лише вкотре прочитати визначення нашого оператора: Оператор instanceof необхідний перевірити, чи був об'єкт, яку посилається змінна X, створено з урахуванням будь-якого класу Y. Оператор у станівідповідає саме походження об'єкта, а не змінної. Тому в прикладі обидва рази на консолі виведе true : у нас об'єкт типу MaineCoon. Звичайно, він був створений на основі класу MaineCoon, але і на основі батьківського класу Catтеж!
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ