JavaRush /Java блог /Java Developer /Оператор Instanceof в Java
Автор
Jesse Haniel
Главный архитектор программного обеспечения в Tribunal de Justiça da Paraíba

Оператор Instanceof в Java

Статья из группы Java Developer
Привет! Сегодня мы поговорим об операторе instanceof, рассмотрим примеры его использования и затронем некоторые связанные с его работой моменты :) На ранних уровнях JavaRush ты уже сталкивался с этим оператором. Помнишь, зачем он нужен? Если нет — не беда, давай вспомним вместе. Оператор instanceof нужен, чтобы проверить, был ли объект, на который ссылается переменная X, создан на основе какого-либо класса Y. Звучит просто. Почему же мы вернулись к этой теме? Прежде всего потому, что теперь ты хорошо знаком с механизмом наследования в Java и остальными принципами ООП. Тема instanceof будет гораздо понятнее, и мы рассмотрим более продвинутые примеры использования. Поехали!Как работает оператор 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 = Хвост Теперь вспомнил? :) Конструктор базового класса, если он есть, всегда вызывается первым при создании любого объекта. 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 тоже!
Комментарии (126)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Denis Bystrov Уровень 1
3 апреля 2024
В Java 16 появилась новая языковая функция — Pattern Matching for instanceof
23 марта 2024
👍🏽
Arslonbek Erkinov Уровень 34
13 марта 2024
спасибо круто
Artem Subbotin Уровень 12
6 марта 2024
Доступно, понятно! Спасибо
Anatoly Уровень 30
4 января 2024
хоршо
Максим Li Уровень 36
13 ноября 2023
Все разъяснили, спасибо!
Айнур Хайбулов Уровень 30 Expert
24 октября 2023
Вроде понятно
Оля23 Уровень 16
18 сентября 2023
👍, все понятно,спасибо
chess.rekrut Уровень 25
28 августа 2023
easy
Max Dudin Уровень 108 Expert
8 августа 2023
Наверное не бедный человек этот Jesse Haniel... с 2008 года архитектор... И что бы мне не начать заниматься этим... ну хотя бы в 2012м?