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

458

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

a instanceof B

или

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

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

Megamug
источник
17
Для записей isInstance () является наиболее удобным методом проверки возможности приведения объекта к типу класса (подробнее см. Tshikatshikaaa.blogspot.nl/2012/07/… )
Jérôme Verstrynge

Ответы:

498

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

Марк Новаковский
источник
12
я не понимаю - пожалуйста, опишите, почему мы не можем писать a instanceof Bref.getClass(). как это может быть принятым ответом с таким небольшим количеством объяснений (или его отсутствием)?
Элиран Малка
65
Синтаксис a instanceof Brefнет a instanceof Bref.class. Второй аргумент оператора instanceof - это имя класса, а не выражение, разрешающее экземпляр объекта класса.
Брэндон Блум
2
да, «динамический», само собой разумеется :) Кроме производительности, это настоящая разница.
Петер
2
@EliranMalka, возможно, у вас может быть класс, который был сгенерирован во время выполнения. Как прокси объекты.
Вагнер Цучия
Итак, в B.class.isAssignableFrom(a.getClass()), B известен, и a instanceof Bлучше. Правильно?
Флориан Ф
208

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

Адам Розенфилд
источник
10
Я не вижу смысла в использовании instanceof / isAssignableFrom с примитивными типами.
Джимми Т.
116

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

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» напрямую. Если иерархия классов является более глубокой и более сложной (например, 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: 12 мс
  • isAssignableFrom: 15ms

Запись

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

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

JBE
источник
6
Да, instanceofэто байт-код, который использует, по сути, ту же логику, что и checkcast(байт-код за преобразованием). Это будет по своей природе быстрее, чем другие варианты, независимо от степени оптимизации JITC.
Hot Licks
1
Что имеет смысл, так как isAssignableFrom()является динамичным.
Матье
да, с JMH результаты совершенно разные (одинаковая скорость для всех).
Юра
Привет, хороший тест, только что столкнулся с ситуацией, когда isAssignableFrom вызывался тысячи раз, переход на instanceof действительно имел значение. Этот ответ стоил бы где-нибудь поста в блоге ...;)
Мартин
33

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

B.class.isInstance(a)

Это работает (возвращает ложь) , если aэто nullслишком.

user102008
источник
Круто, но это не отвечает на вопрос и должно было быть комментарием.
Madbreaks
23

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

Читайте 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.
Ашиш Арья
источник
3
b instanceof Aэквивалентно A.class.isAssignableFrom(b.getClass())(как заметил ОП). Ваш пример верный, но не имеет значения.
Кару
Так как new Y()может быть недопустимым, если Yон абстрактный или без общедоступного конструктора по умолчанию, вы можете сказать, что X x = (Y)nullон допустим тогда и только тогда, когда он x.getClass().isAssignableFrom(Y.class)равен true.
Земля Двигатель
2
Почему в этом примере b.getClass (). IsAssignableFrom (A.class)? Я думаю, что пример должен быть обратным A.class.isAssignableFrom (b.getClass ()).
Лошадь втапках
14

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

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

null.getClass (). isAssignableFrom (X) сгенерирует исключение NullPointerException

С. Али Токмен
источник
4
-1, неверно: null instanceof X(где X - некоторый класс, известный во время компиляции) всегда будет возвращаться false.
Каспар
4
@ Каспар, пока ты прав, основная идея была хорошей идеей. Я отредактировал пост так, что он правильный.
Эриксон
1
это полезно, крайний случай всегда важен :).
триллионы
Чтобы быть эквивалентной первой строке, вторая строка должна быть X.class.isAssignableFrom(null.getClass())не так ли? Но да, вызов getClass()нулевой ссылки приведет к NPE.
Уильям Прайс
Этот ответ не учитывает смысла - нулевая разыменование не имеет значения, потому что сбой происходит вне операции (вам всегда нужно проверять нулевое значение, прежде чем использовать такую ​​ссылку). Вообще getClass()не следует использовать с isAssignableFromв первую очередь - операция предназначена для ситуации отсутствия объектов. Если у вас есть ссылка на объект a, использовать a instanceof SomeClass(если вы делаете знать тип SomeClass) или someObject.getClass().isInstance(a)(если вы не знаете тип someObject).
AndrewF
12

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

Ой, я вижу, этот ответ уже покрыт. Может быть, этот пример кому-то полезен.

tkalmijn
источник
3
на самом деле ни один ответ не является действительно правильным isAssignableFrom работы с классами, Class.isInstance является аналогом 'instanceof'
bestsss
Чтобы поместить правильный комментарий @ bestsss в конкретный код: поскольку у вас есть объект ( this), clazz.isInstance(this)будет лучше в вашем примере.
AndrewF
7

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

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

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

Оуэн
источник
5

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

//Define Class< T > type ... 

Object e = new Object();

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

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

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

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
Джеймс Дринкард
источник
4

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

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

ИЛИ

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

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

алгебра
источник
2
Это неправда. Пожалуйста, смотрите комментарий «Адама Розенфилда» stackoverflow.com/questions/496928/…
Максим Векслер
1
Не могли бы вы уточнить "Это не правда"? Комментарий, на который вы ссылаетесь, не имеет никакого отношения к сценарию в моем посте. У меня есть тестовый код, который поддерживает мое объяснение.
алгебра
Если у вас есть ненулевая ссылка на экземпляр объекта ( objв этом примере) любого типа, вы можете вызвать открытый getClass()метод для него, чтобы получить метаданные отражения для реализующего класса. Это верно, даже если этот реализующий тип класса не будет юридически видим в этом месте во время компиляции. Это нормально , потому что во время выполнения, для вас , чтобы держать objссылку, некоторый код путь , который в конечном счете сделал иметь законный доступ к классу создал один и дал (утечка?) Его вам.
Уильям Прайс
0
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, он не полностью отключен.

Стефан Корсхольм
источник
2
Пожалуйста, объясните, что делает этот код и как он отвечает на вопрос.
Фонд Моника иск
0

Разговор с точки зрения производительности "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% времени выполнения). Так что нет никакой разницы, что бы вы ни выбрали

Yura
источник
0

Как насчет некоторых примеров, чтобы показать это в действии ...

@Test
public void isInstanceOf() {
    Exception anEx1 = new Exception("ex");
    Exception anEx2 = new RuntimeException("ex");
    RuntimeException anEx3 = new RuntimeException("ex");

    //Base case, handles inheritance
    Assert.assertTrue(anEx1 instanceof Exception);
    Assert.assertTrue(anEx2 instanceof Exception);
    Assert.assertTrue(anEx3 instanceof Exception);

    //Other cases
    Assert.assertFalse(anEx1 instanceof RuntimeException);
    Assert.assertTrue(anEx2 instanceof RuntimeException);
    Assert.assertTrue(anEx3 instanceof RuntimeException);
}

@Test
public void isAssignableFrom() {
    Exception anEx1 = new Exception("ex");
    Exception anEx2 = new RuntimeException("ex");
    RuntimeException anEx3 = new RuntimeException("ex");

    //Correct usage = The base class goes first
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx1.getClass()));
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx2.getClass()));
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx3.getClass()));

    //Incorrect usage = Method parameter is used in the wrong order
    Assert.assertTrue(anEx1.getClass().isAssignableFrom(Exception.class));
    Assert.assertFalse(anEx2.getClass().isAssignableFrom(Exception.class));
    Assert.assertFalse(anEx3.getClass().isAssignableFrom(Exception.class));
}
Саган
источник
-2

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

Милан
источник
13
Хм, если у вас есть узкое место instanceof, я думаю, у вас есть серьезные проблемы с дизайном ...
Sleske
1
Ответ JBE представляет гипотезу, которая отличается от вашей гипотезы.
Аластор Муди