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

Какое из следующего лучше?

a instanceof B

или

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

TL; DR

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

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

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

Тест

Кредит: Как советовал @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: 6ms
  • isInstance: 12ms
  • isAssignableFrom: 15ms

Примечание

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

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

Ответ 4

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

B.class.isInstance(a)

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

Ответ 5

Помимо основных различий, упомянутых выше, существует ключевая тонкая разница между экземпляром оператора и 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.

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

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

ИЛИ

... obj instanceof A ...

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

Ответ 10

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

//Define Class< T > type ... 

Object e = new Object();

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

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

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

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

Ответ 11

isAssignableFrom(A, B) =

if (A == B) return true
else if (B == java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))

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

Ответ 12

Говоря о производительности "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

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

Ответ 13

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