В чем разница между instanceof и Class.isAssignableFrom (…)?

Что из следующего лучше?

a instanceof B 

или

 B.class.isAssignableFrom(a.getClass()) 

Единственное отличие, о котором я знаю, когда «a» равно null, первое возвращает false, а второе – исключение. Кроме этого, они всегда дают одинаковый результат?

13 Solutions collect form web for “В чем разница между instanceof и Class.isAssignableFrom (…)?”

При использовании instanceof вам нужно знать class B во время компиляции. При использовании isAssignableFrom() он может быть динамическим и изменяться во время выполнения.

instanceof может использоваться только со ссылочными типами, а не с примитивными типами. isAssignableFrom() может использоваться с любыми объектами classа:

 a instanceof int // syntax error 3 instanceof Foo // syntax error int.class.isAssignableFrom(int.class) // true 

См. http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class) .

Говоря о производительности:

TL; DR

Используйте isInstance или instanceof, которые имеют сходную производительность. isAssignableFrom немного медленнее.

Отсортировано по производительности:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isAssignableFrom (+ 2.7%)

На основе эталона 2000 итераций на JAVA 8 Windows x64 с 20 итерациями прогрева.

В теории

Используя мягкий, как просмотрщик байт-кодов, мы можем перевести каждый оператор в байт-код.

В контексте:

 package foo; public class Benchmark { public static final Object a = new A(); public static final Object b = new B(); ... } 

ЯВА:

 b instanceof A; 

Bytecode:

 getstatic foo/Benchmark.b:java.lang.Object instanceof foo/A 

ЯВА:

 A.class.isInstance(b); 

Bytecode:

 ldc Lfoo/A; (org.objectweb.asm.Type) getstatic foo/Benchmark.b:java.lang.Object invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z); 

ЯВА:

 A.class.isAssignableFrom(b.getClass()); 

Bytecode:

 ldc Lfoo/A; (org.objectweb.asm.Type) getstatic foo/Benchmark.b:java.lang.Object invokevirtual java/lang/Object getClass(()Ljava/lang/Class;); invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z); 

Измеряя, сколько команд байт-кода используется каждым оператором, мы могли бы ожидать, что instanceof и isInstance будут быстрее, чем isAssignableFrom . Однако фактическая производительность НЕ определяется байтовым кодом, а машинным кодом (который зависит от платформы). Давайте сделаем микро-тест для каждого из операторов.

Тест

Кредит: Как пояснил @ aleksandr-dubinsky, и благодаря @yura для предоставления базового кода, вот тестовый пример JMH (см. Это руководство по настройке ):

 class A {} class B extends A {} public class Benchmark { public static final Object a = new A(); public static final Object b = new B(); @Benchmark @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MICROSECONDS) public boolean testInstanceOf() { return b instanceof A; } @Benchmark @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MICROSECONDS) public boolean testIsInstance() { return A.class.isInstance(b); } @Benchmark @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MICROSECONDS) public boolean testIsAssignableFrom() { return A.class.isAssignableFrom(b.getClass()); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(TestPerf2.class.getSimpleName()) .warmupIterations(20) .measurementIterations(2000) .forks(1) .build(); new Runner(opt).run(); } } 

Получили следующие результаты (оценка – это количество операций в единице времени , поэтому чем выше оценка, тем лучше):

 Benchmark Mode Cnt Score Error Units Benchmark.testIsInstance thrpt 2000 373,061 ± 0,115 ops/us Benchmark.testInstanceOf thrpt 2000 371,047 ± 0,131 ops/us Benchmark.testIsAssignableFrom thrpt 2000 363,648 ± 0,289 ops/us 

Предупреждение

  • эталонный показатель зависит от JVM и платформы. Поскольку между каждой операцией нет существенных различий, возможно, получится другой результат (и, возможно, другой порядок!) На другой версии JAVA и / или таких платформах, как Solaris, Mac или Linux.
  • в тесте сравнивается производительность «B является экземпляром A», когда «B расширяет A» напрямую. Если иерархия classов более глубокая и сложная (например, B расширяет X, который расширяет Y, который расширяет Z, который расширяет A), результаты могут быть разными.
  • обычно рекомендуется написать код, сначала выбирающий один из операторов (наиболее удобный), а затем профиль вашего кода, чтобы проверить, есть ли узкое место в производительности. Возможно, этот оператор пренебрежимо мал в контексте вашего кода или, может быть, …
  • по отношению к предыдущей точке, instanceof в контексте вашего кода может быть оптимизирован легче, чем isInstance например …

Чтобы привести пример, выполните следующий цикл:

 class A{} class B extends A{} A b = new B(); boolean execute(){ return A.class.isAssignableFrom(b.getClass()); // return A.class.isInstance(b); // return b instanceof A; } // Warmup the code for (int i = 0; i < 100; ++i) execute(); // Time it int count = 100000; final long start = System.nanoTime(); for(int i=0; i 

Благодаря JIT, код в какой-то момент оптимизирован, и мы получаем:

  • instanceof: 6ms
  • isInstance: 12 мс
  • isAssignableFrom: 15ms

Заметка

Первоначально этот пост выполнял свой собственный бенчмарк, используя цикл for в сыром JAVA, что давало ненадежные результаты, так как некоторые оптимизации, такие как Just In Time, могут устранить цикл. Таким образом, в основном было измерено, как долго выполнялся компилятор JIT для оптимизации цикла: см. Тест производительности независимо от количества итераций для более подробной информации

Связанные вопросы

  • Оператор instanceof генерирует много накладных расходов? Зачем?
  • Как экземпляр реализован внутри JAVA?
  • Влияние производительности использования instanceof в Java

Более прямым эквивалентом a instanceof B является

 B.class.isInstance(a) 

Это работает (возвращает false), когда a равно null .

Помимо основных различий, упомянутых выше, существует ключевая тонкая разница между экземпляром оператора и isAssignableFrom методом classа.

Прочитайте instanceof как «это (левая часть) экземпляр этого или любого подclassа этого (правая часть)» и прочитайте x.getClass().isAssignableFrom(Y.class) как «Могу ли я написать X x = new Y() “. Другими словами, оператор instanceof проверяет, является ли левый объект тем же или подclassом правого classа, а isAssignableFrom проверяет, можно ли присвоить объект classа параметра (from) ссылке на class, на который вызывается метод.
Обратите внимание, что оба они рассматривают фактический экземпляр, а не ссылочный тип.

Рассмотрим пример из 3 classов A, B и C, где C продолжается B и B продолжается A.

 B b = new C(); System.out.println(b instanceof A); //is b (which is actually class C object) instance of A, yes. This will return true. System.out.println(b instanceof B); // is b (which is actually class C object) instance of B, yes. This will return true. System.out.println(b instanceof C); // is b (which is actually class C object) instance of C, yes. This will return true. If the first statement would be B b = new B(), this would have been false. System.out.println(b.getClass().isAssignableFrom(A.class));//Can I write C c = new A(), no. So this is false. System.out.println(b.getClass().isAssignableFrom(B.class)); //Can I write C c = new B(), no. So this is false. System.out.println(b.getClass().isAssignableFrom(C.class)); //Can I write C c = new C(), Yes. So this is true. 

Существует и другая разница:

null instanceof X является false независимо от того, что X является

null.getClass (). isAssignableFrom (X) вызовет исключение NullPointerException

Есть еще одна разница. Если тип (class) для тестирования является динамическим, например, передается как параметр метода, то instanceof не будет его обрезать.

 boolean test(Class clazz) { return (this instanceof clazz); // clazz cannot be resolved to a type. } 

но вы можете сделать:

 boolean test(Class clazz) { return (clazz.isAssignableFrom(this.getClass())); // okidoki } 

К сожалению, я вижу, что этот ответ уже рассмотрен. Может быть, этот пример полезен кому-то.

Этот stream дал мне некоторое представление о том, как instanceof отличается от isAssignableFrom , поэтому я думал, что поделюсь чем-то своим.

Я обнаружил, что использование isAssignableFrom является единственным (возможно, не единственным, но, возможно, самым простым) способом спросить себя, если ссылка одного classа может принимать экземпляры другого, если у вас нет экземпляров ни одного classа для сравнения.

Следовательно, я не нашел использование оператора instanceof для сравнения назначаемости, чтобы быть хорошей идеей, когда все, что у меня было, было classами, если только я не планировал создать экземпляр из одного из classов; Я думал, что это будет неряшливо.

Рассмотрим следующую ситуацию. Предположим, вы хотите проверить, является ли тип A суперclassом типа obj, вы можете пойти либо

… A.class.isAssignableFrom (obj.getClass ()) …

ИЛИ

… obj instanceof A …

Но решение isAssignableFrom требует, чтобы вид obj был видимым. Если это не так (например, тип obj может быть частного внутреннего classа), этот параметр отсутствует. Однако решение instanceof всегда будет работать.

instanceof не может использоваться с примитивными типами или типовыми типами. Как и в следующем коде:

 //Define Class< T > type ... Object e = new Object(); if(e instanceof T) { // Do something. } 

Ошибка: Невозможно выполнить проверку экземпляра с параметром типа Т. Используйте вместо этого объект стирания, так как информация об универсальном типе будет удалена во время выполнения.

Не компилируется из-за стирания типа, удаляя ссылку на runtime. Однако приведенный ниже код будет скомпилирован:

 if( type.isAssignableFrom(e.getClass())){ // Do something. } 
 isAssignableFrom(A, B) = if (A == B) return true else if (B == java.lang.Object) return false else return isAssignableFrom(A, getSuperClass(B)) 

Псевдо-код выше – это определение, если ссылки типа / classа A назначаются из ссылок типа / classа B. Это рекурсивное определение. Некоторым это может быть полезно, для других это может запутать. Я добавляю его на случай, если кто-то найдет его полезным. Это всего лишь попытка захватить мое понимание, это не официальное определение. Он используется в определенной реализации Java VM и работает для многих примеров программ, поэтому пока я не могу гарантировать, что он захватывает все аспекты isAssignableFrom, он не полностью отключен.

Говоря с точки зрения производительности «2» (с JMH):

 class A{} class B extends A{} public class InstanceOfTest { public static final Object a = new A(); public static final Object b = new B(); @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public boolean testInstanceOf() { return b instanceof A; } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public boolean testIsInstance() { return A.class.isInstance(b); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public boolean testIsAssignableFrom() { return A.class.isAssignableFrom(b.getClass()); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(InstanceOfTest.class.getSimpleName()) .warmupIterations(5) .measurementIterations(5) .forks(1) .build(); new Runner(opt).run(); } } 

Это дает:

 Benchmark Mode Cnt Score Error Units InstanceOfTest.testInstanceOf avgt 5 1,972 ? 0,002 ns/op InstanceOfTest.testIsAssignableFrom avgt 5 1,991 ? 0,004 ns/op InstanceOfTest.testIsInstance avgt 5 1,972 ? 0,003 ns/op 

Итак, мы можем заключить: instanceof так же быстро, как isInstance () и isAssignableFrom () не за горами (+ 0,9% времени выполнения). Так что неважно, что бы вы ни выбрали

некоторые тесты, которые мы сделали в нашей команде, показывают, что A.class.isAssignableFrom(B.getClass()) работает быстрее, чем B instanceof A это может быть очень полезно, если вам нужно проверить это на большом количестве элементов.

  • Избегание instanceof в Java
  • Как проверить, является ли тип примитивным
  • GetMethod для общего метода
  • Как десериализация WCF создает объекты без вызова конструктора?
  • Как получить свойство Static с reflectionм
  • Использование отражения в C # для получения свойств вложенного объекта
  • Почему отраженный доступ к защищенному / частному члену classа в C #?
  • Использование Case / Switch и GetType для определения объекта
  • Получить экземпляр нового объекта из типа
  • Как я могу отражать элементы динамического объекта?
  • используя objc_msgSend для вызова функции Objective C с именованными аргументами
  • Давайте будем гением компьютера.