В чем разница между instanceof и Class.isAssignableFrom (...)? - PullRequest
428 голосов
/ 30 января 2009

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

a instanceof B

или

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

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

Ответы [ 13 ]

466 голосов
/ 30 января 2009

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

201 голосов
/ 30 января 2009

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

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).

106 голосов
/ 24 августа 2012

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

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();

  ...

}

JAVA:

b instanceof A;

Bytecode:

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

JAVA:

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);

JAVA:

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 . Однако фактическая производительность определяется не байт-кодом, а машинным кодом (который зависит от платформы). Давайте сделаем микро-тест для каждого из операторов.

Тест

Credit: По совету @ 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" напрямую. Если иерархия классов является более глубокой и более сложной (например, 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<count; i++){
   execute();
}
final long elapsed = System.nanoTime() - start;

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

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

Примечание

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

Смежные вопросы

33 голосов
/ 16 августа 2011

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

B.class.isInstance(a)

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

22 голосов
/ 07 сентября 2010

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

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

Рассмотрим пример 3 классов 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.
15 голосов
/ 10 декабря 2010

Есть и еще одно отличие:

Нулевой экземпляр X равен false независимо от того, что X

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

11 голосов
/ 25 января 2011

Есть еще одно отличие. Если тип (класс) для проверки является динамическим, например, переданный в качестве параметра метода, 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
}

Упс, я вижу, этот ответ уже покрыт. Может быть, этот пример кому-нибудь пригодится.

7 голосов
/ 08 декабря 2011

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

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

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

4 голосов
/ 18 марта 2010

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

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

OR

... объект A ...

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

3 голосов
/ 09 сентября 2013

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

//Define Class< T > type ... 

Object e = new Object();

if(e instanceof T) {
  // Do something.
}

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

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

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
...