1. Введение
В этом кратком руководстве мы рассмотрим разницу между instanceof
, Class.isInstance
и Class.isAssignableFrom
. Мы узнаем, как использовать каждый метод и в чем разница между ними.
2. Настройка
Давайте настроим интерфейс и пару классов, которые будем использовать , изучая функциональность instanceof
, Class.isInstance
и Class.isAssignableFrom
.
Во-первых, давайте определим интерфейс:
public interface Shape {
}
Далее определим класс, реализующий Shape
:
public class Triangle implements Shape {
}
Теперь мы создадим класс, который расширяет Triangle
:
public class IsoscelesTriangle extends Triangle {
}
3. экземпляр
Ключевое слово instanceof
— это бинарный оператор, и мы можем использовать его, чтобы проверить, является ли определенный объект экземпляром данного типа. Следовательно, результат операции либо истина
, либо ложь
. Кроме того, ключевое слово instanceof
является наиболее распространенным и простым способом проверки того, является ли объект подтипом другого типа.
Давайте используем наши классы с оператором instanceof :
Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Shape nonspecificShape = null;
assertTrue(shape instanceof Shape);
assertTrue(triangle instanceof Shape);
assertTrue(isoscelesTriangle instanceof Shape);
assertFalse(nonspecificShape instanceof Shape);
assertTrue(shape instanceof Triangle);
assertTrue(triangle instanceof Triangle);
assertTrue(isoscelesTriangle instanceof Triangle);
assertFalse(nonspecificShape instanceof Triangle);
assertFalse(shape instanceof IsoscelesTriangle);
assertFalse(triangle instanceof IsoscelesTriangle);
assertTrue(isoscelesTriangle instanceof IsoscelesTriangle);
assertFalse(nonspecificShape instanceof IsoscelesTriangle);
В приведенном выше фрагменте кода мы видим, что правосторонний тип является более общим, чем левосторонний объект . В частности, оператор instanceof
будет обрабатывать нулевые
значения в false
.
4. Класс.isInstance
Метод isInstance
класса Class
эквивалентен оператору instanceof .
Метод isInstance
появился в Java 1.1, поскольку его можно использовать динамически. Как правило, этот метод возвращает значение true
, если аргумент не равен null
и может быть успешно приведен к ссылочному типу без создания ClassCastException
.
Давайте посмотрим, как мы можем использовать метод isInstance
с интерфейсом и классами, которые мы определили:
Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Triangle isoscelesTriangle2 = new IsoscelesTriangle();
Shape nonspecificShape = null;
assertTrue(Shape.class.isInstance(shape));
assertTrue(Shape.class.isInstance(triangle));
assertTrue(Shape.class.isInstance(isoscelesTriangle));
assertTrue(Shape.class.isInstance(isoscelesTriangle2));
assertFalse(Shape.class.isInstance(nonspecificShape));
assertTrue(Triangle.class.isInstance(shape));
assertTrue(Triangle.class.isInstance(triangle));
assertTrue(Triangle.class.isInstance(isoscelesTriangle));
assertTrue(Triangle.class.isInstance(isoscelesTriangle2));
assertFalse(IsoscelesTriangle.class.isInstance(shape));
assertFalse(IsoscelesTriangle.class.isInstance(triangle));
assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle));
assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle2));
Как мы видим, правая часть может быть эквивалентна или более конкретна, чем левая часть . В частности, предоставление null
методу isInstance
возвращает false
.
5. Класс.isAssignableFrom
Метод Class.isAssignableFrom
вернет значение true
, если класс
в левой части оператора совпадает или является суперклассом или суперинтерфейсом предоставленного параметра класса
.
Давайте воспользуемся нашими классами с методом isAssignableFrom
:
Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Triangle isoscelesTriangle2 = new IsoscelesTriangle();
assertFalse(shape.getClass().isAssignableFrom(Shape.class));
assertTrue(shape.getClass().isAssignableFrom(shape.getClass()));
assertTrue(shape.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
assertFalse(triangle.getClass().isAssignableFrom(Shape.class));
assertTrue(triangle.getClass().isAssignableFrom(shape.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(Shape.class));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(shape.getClass()));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(Shape.class));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(shape.getClass()));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
Как и в примере с isInstance
, мы ясно видим, что правая часть должна быть либо такой же, либо более конкретной, чем левая часть. Мы также можем видеть, что мы никогда не можем назначить наш интерфейс Shape .
6. Различия
Теперь, когда мы изложили несколько подробных примеров, давайте рассмотрим некоторые различия.
6.1. Семантические различия
На первый взгляд, instanceof
— это ключевое слово языка Java. Напротив, как isInstance
, так и isAssignableFrom
являются нативными методами типа Class
.
Семантически мы используем их для проверки различных отношений между двумя элементами программирования:
- Два объекта: мы можем проверить, являются ли два объекта идентичными или равными.
- Объект и тип: мы можем проверить, является ли объект экземпляром типа. Очевидно, что и ключевое слово
instanceof , и метод
isInstance
относятся к этой категории. - Два типа: мы можем проверить, совместим ли один тип с другим типом, например с методом
isAssignableFrom
.
6.2. Различия в использовании угловых корпусов
Во-первых, они отличаются нулевым
значением:
assertFalse(null instanceof Shape);
assertFalse(Shape.class.isInstance(null));
assertFalse(Shape.class.isAssignableFrom(null)); // NullPointerException
Из приведенного выше фрагмента кода и instanceof
, и isInstance
возвращают false
; однако метод isAssignableFrom
выдает NullPointerException
.
Во-вторых, они различаются примитивными типами:
assertFalse(10 instanceof int); // illegal
assertFalse(int.class.isInstance(10));
assertTrue(Integer.class.isInstance(10));
assertTrue(int.class.isAssignableFrom(int.class));
assertFalse(float.class.isAssignableFrom(int.class));
Как мы видим, ключевое слово instanceof
не поддерживает примитивные типы. Если мы используем метод isInstance
со значением int
, компилятор Java преобразует значение int
в объект Integer
. Итак, метод isInstance
поддерживает примитивные типы, но всегда возвращает false
. Когда мы используем метод isAssignableFrom
, результат зависит от конкретных значений типа.
В-третьих, они отличаются переменными экземпляра класса:
Shape shape = new Triangle();
Triangle triangle = new Triangle();
Class<?> clazz = shape.getClass();
assertFalse(triangle instanceof clazz); // illegal
assertTrue(clazz.isInstance(triangle));
assertTrue(clazz.isAssignableFrom(triangle.getClass()));
Из приведенного выше фрагмента кода мы понимаем, что методы isInstance
и isAssignableFrom
поддерживают переменные экземпляра класса, а ключевое слово instanceof
— нет.
6.3. Различия в байт-коде
В скомпилированном файле класса они используют разные коды операций:
- Ключевое слово
instanceof
соответствует опкодуinstanceof
- Оба
метода isInstance
иisAssignableFrom
будут использовать код операцииinvokevirtual.
В наборе инструкций JVM код операции instanceof
имеет значение 193 и имеет двухбайтовый операнд:
instanceof
indexbyte1
indexbyte2
Затем JVM вычислит (indexbyte1 << 8) | indexbyte2
в индекс
. И этот индекс
указывает на пул констант времени выполнения текущего класса. В индексе пул констант времени выполнения содержит символическую ссылку на константу CONSTANT_Class_info .
И эта константа является именно тем значением, которое нужно правой части ключевого слова instanceof .
Кроме того, это также объясняет, почему ключевое слово instanceof
не может использовать переменную экземпляра класса. Это связано с тем, что для кода операции instanceof
требуется постоянный тип в пуле констант времени выполнения, и мы не можем заменить постоянный тип переменной экземпляра класса.
И где хранится информация об объекте с левой стороны ключевого слова instanceof
? Он находится на вершине стека операндов. Итак, для ключевого слова instanceof
требуется объект в стеке операндов и константный тип в пуле констант времени выполнения.
В наборе инструкций JVM код операции invokevirtual
имеет значение 182, а также имеет двухбайтовый операнд:
invokevirtual
indexbyte1
indexbyte2
Точно так же JVM будет вычислять (indexbyte1 << 8) | indexbyte2
в индекс
. По индексу
пул констант времени выполнения содержит символическую ссылку на константу CONSTANT_Methodref_info .
Эта константа содержит информацию о целевом методе, такую как имя класса, имя метода и дескриптор метода.
Метод isInstance
требует наличия двух элементов в стеке операндов: первый элемент — это тип; второй элемент - это объект. Однако метод isAssignableFrom
требует двух элементов типа в стеке операндов.
6.4. Подводя итоги
Подводя итог, давайте воспользуемся таблицей, чтобы проиллюстрировать их различия:
| Имущество | `случай` | `Класс.isInstance` | `Класс.isAssignableFrom` |
| Добрый | ключевое слово | собственный метод | собственный метод |
| Операнды | Объект и тип | Тип и объект | Один тип и другой тип |
| Нулевая обработка | `ЛОЖЬ` | `ЛОЖЬ` | `Исключение нулевого указателя` |
| Примитивные типы | Не поддерживается | Поддерживается, но всегда `ложно` | Да |
| Переменная экземпляра класса | Нет | Да | Да |
| Байт-код | `случай` | `вызыватьвиртуальный` | `вызыватьвиртуальный` |
| Наиболее подходит, когда | Объект задан, тип известен во время компиляции | Объект задан, целевой тип неизвестен в типе компиляции | Объект не указан, известны только типы и только во время выполнения |
| Сценарии использования | Ежедневное использование, подходит для большинства случаев | Сложные и нетипичные случаи, такие как реализация библиотеки или утилиты с использованием Reflection API |
7. Заключение
В этом руководстве мы рассмотрели методы instanceof
, Class.isInstance
и Class.isAssignableFrom
и изучили их использование и различия.
Как всегда, пример кода для этого руководства можно найти на GitHub .