Перейти к основному содержимому

Class.isInstance против Class.isAssignableFrom и instanceof

· 7 мин. чтения

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 .