Оператор instanceof в Java
Проверяйте тип объекта во время выполнения с помощью instanceof и используйте сопоставление с образцом для лаконичного кода.
instanceof задаёт вопрос во время выполнения: «действительно ли эта ссылка является T (или подтипом T)?» Ответ — boolean, а современная форма с сопоставлением с образцом также привязывает значение к типизированной переменной за один шаг, что избавляет от необходимости отдельного приведения типа.
Это инструмент, к которому обращаются, когда есть полиморфная ссылка и нужно выяснить, каким конкретным типом она является в действительности — как правило, внутри equals, при обходе гетерогенной структуры данных или при работе с запечатанными иерархиями.
Базовая форма
Классический синтаксис: expression instanceof Type:
Object o = "hello";
if (o instanceof String) {
String s = (String) o;
System.out.println(s.length());
}Проверка возвращает true, если o ссылается на String или любой её подтип, и false, если o равно null или указывает на объект другого типа. null instanceof Anything всегда false — небольшая, но полезная гарантия.
Сопоставление с образцом для instanceof
Начиная с Java 16, instanceof принимает шаблон типа, объявляющий переменную, привязанную к суженному типу, прямо в том же выражении:
if (o instanceof String s) {
System.out.println(s.length()); // no cast needed
}Если проверка успешна, s находится в области видимости и уже типизирована как String. Если нет — s вне области видимости. Приведение типа исчезает, избыточность исчезает, и нельзя случайно привести то, что проверка отвергла.
Область видимости привязанной переменной
Привязанная переменная находится в области видимости везде, где компилятор может доказать, что проверка прошла успешно. Это включает ветку if, а также распространяется через && и в отрицание if:
if (o instanceof String s && s.length() > 3) { ... } // s is in scope after && — also a String
if (!(o instanceof String s)) return;
System.out.println(s.length()); // s is in scope after the early returnВторой шаблон особенно удобен для кода в стиле guard-clause — сузить тип, выйти, если не совпало, и свободно использовать привязанное имя ниже.
Обратная сторона — ||: привязка не распространяется через него, потому что правая часть выполняется именно тогда, когда проверка провалилась. o instanceof String s || s.length() > 0 не скомпилируется — s не находится в области видимости справа от ||.
Ограничения на целевой тип
Компилятор отклоняет проверки, которые он может доказать невозможными. "hello" instanceof Integer даже не скомпилируется, потому что String и Integer не связаны между собой. Это позволяет обнаруживать опечатки и остатки рефакторинга во время сборки, а не во время выполнения.
Также отклоняются повышающие приведения, которые не могут завершиться неудачей: Object o = ...; if (o instanceof Object) {} помечается как избыточное.
В switch
Тот же механизм сопоставления с образцом доступен в switch, где он действительно раскрывается при работе с запечатанными иерархиями:
String describe(Object o) {
return switch (o) {
case Integer i -> "int " + i;
case String s -> "str of length " + s.length();
case int[] a -> "array of " + a.length;
case null -> "nothing";
default -> "something else";
};
}При использовании запечатанного типа в качестве селектора можно убрать default, и компилятор потребует case для каждого разрешённого подтипа — совместите это с запечатанными классами, чтобы получить исчерпывающий анализ вариантов.
Когда использовать — и когда не стоит
Используйте instanceof, когда тип действительно неизвестен и ответ меняет поведение: при реализации equals, обработке запечатанных иерархий с тегированными объединениями, обходе узлов AST. Не используйте его как замену полноценного полиморфизма — цепочка if (x instanceof A) ... else if (x instanceof B) ... над открытой иерархией обычно сигнализирует о том, что виртуальный метод на типе справился бы лучше.
Рабочий пример
Что дальше
instanceof — один из нескольких методов, на которые отвечает каждый объект Java. Следующая глава даёт более широкий взгляд на весь пакет — java.lang.Object, корневой класс, который каждый тип молча расширяет, и методы, которые вы наследуете от него, хотите вы того или нет. Продолжите с класса Object в Java.