Привіт! Сьогодні ми поговоримо про оператор instanceof, розглянемо приклади його використання та зачепимо деякі моменти, пов'язані з його роботою :)
На ранніх рівнях JavaRush ти вже зустрічав цей оператор. Пам'ятаєш, навіщо він потрібен? Якщо ні — не біда, давай пригадаємо разом.
Оператор instanceof потрібен, щоб перевірити, чи був об'єкт, на який посилається змінна X, створений на основі якогось класу Y.
Звучить просто. Чому ж ми повернулися до цієї теми?
Перш за все тому, що тепер ти добре знайомий із механізмом наслідування в Java й іншими принципами ООП. Тема instanceof буде зрозумілішою, і ми розглянемо більш просунуті приклади використання.
Починаємо!
Ти напевно пам'ятаєш, що оператор instanceof повертає значення true, якщо перевірка показала істинність, або false, якщо результат був хибним.
Відповідно, найчастіше він трапляється в різного роду перевірочних умовах (
Наприклад, як думаєш, що видасть ось така перевірка:
Ти напевно пам'ятаєш, що оператор 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, коли ми перевіряємо належність об'єкта до якогось класу в звичайній ситуації, але що буде, якщо ми додамо сюди відношення «батько-нащадок»?
Наприклад, як думаєш, що видасть ось така перевірка:
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 = Хвіст
Тепер згадав? :)
Конструктор базового класу, якщо він є, завжди викликається першим під час створення будь-якого об'єкта. Instanceof керується саме цим принципом, коли намагається визначити, чи був об'єкт А створений на основі класу Б. Якщо конструктор базового класу викликаний, значить жодних сумнівів бути не може.
З другою перевіркою все простіше:
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);
Що буде перевіряти instanceof: нашу змінну класу Cat чи наш об'єкт класу MaineCoon? Насправді, відповісти на це запитання просто. Треба всього лише ще раз прочитати визначення нашого оператора:
Оператор instanceof потрібен для того, щоб перевірити, чи був об'єкт, на який посилається змінна X, створений на основі якогось класу Y.
Оператор instanceof перевіряє саме походження об'єкта, а не змінної.
Тому в прикладі обидва рази в консолі виведе true: у нас об'єкт типу MaineCoon. Звісно, він був створений на основі класу MaineCoon, але й на основі батьківського класу Cat теж!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ