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

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

a instanceof B

or

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

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


person Megamug    schedule 30.01.2009    source источник
comment
Для записей isInstance () - наиболее удобный метод проверки, можно ли преобразовать объект в тип класса (подробнее см .: tshikatshikaaa.blogspot.nl/2012/07/)   -  person Jérôme Verstrynge    schedule 30.07.2012


Ответы (14)


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

person Marc Novakowski    schedule 30.01.2009
comment
я не понимаю - пожалуйста, поясните, почему мы не можем писать a instanceof Bref.getClass(). как это может быть принятый ответ с таким кратким объяснением (или его отсутствием)? - person Eliran Malka; 28.03.2013
comment
Синтаксис - a instanceof Bref, а не a instanceof Bref.class. Второй аргумент оператора instanceof - это имя класса, а не выражение, разрешающееся в экземпляр объекта класса. - person Brandon Bloom; 16.04.2013
comment
да, динамика само собой разумеется :) Если не считать производительности, это настоящая разница. - person peterk; 21.06.2013
comment
@EliranMalka, возможно, у вас может быть класс, созданный во время выполнения. Как объекты прокси. - person Wagner Tsuchiya; 11.05.2016
comment
Итак, в B.class.isAssignableFrom(a.getClass()) B известен, а a instanceof B лучше. Правильно? - person Florian F; 05.12.2019
comment
Если у вас есть объект a и вы знаете тип B при компиляции, используйте a instanceof B. Если у вас есть a, и вы не знаете тип B, но у вас есть объект b, используйте b.getClass().isInstance(a). Если у вас есть a, а у вас нет объекта, а есть Class<?> someBClass, используйте someBClass.isInstance(a). Если у вас есть два Class<?>, но нет фактических экземпляров объектов этих типов, это одно условие возникает при использовании someBClass.isAssignableFrom(someAClass). - person AndrewF; 03.04.2020

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

person Adam Rosenfield    schedule 30.01.2009
comment
Я не вижу смысла использовать instanceof / isAssignableFrom с примитивными типами. - person Jimmy T.; 25.03.2016

Если говорить о производительности:

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;

Байт-код:

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

ЯВА:

A.class.isInstance(b);

Байт-код:

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

Байт-код:

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: 15 мс

Примечание

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

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

person JBE    schedule 24.08.2012
comment
Да, instanceof - это байт-код, который использует, по сути, ту же логику, что и checkcast (байт-код, стоящий за преобразованием). По сути, он будет быстрее, чем другие варианты, независимо от степени оптимизации JITC. - person Hot Licks; 05.07.2013
comment
Это имеет смысл, поскольку isAssignableFrom() динамический. - person Matthieu; 26.04.2016
comment
да, с JMH результаты совершенно разные (скорость у всех одинаковая). - person Yura; 20.08.2017
comment
Привет, хороший тест, только что столкнулся с ситуацией, когда isAssignableFrom вызывался тысячи раз, переход на instanceof действительно имел значение. Этот ответ стоил бы где-нибудь в блоге ...;) - person Martin; 06.03.2019

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

B.class.isInstance(a)

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

person user102008    schedule 15.08.2011
comment
Круто, но это не ответ на вопрос и должен был быть комментарием. - person Madbreaks; 18.09.2019

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

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

Рассмотрим пример трех классов 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.
person Ashish Arya    schedule 07.09.2010
comment
b instanceof A эквивалентен A.class.isAssignableFrom(b.getClass()) (как заметил OP). Ваш пример правильный, но неуместный. - person Karu; 12.04.2012
comment
Поскольку new Y() может быть незаконным, если Y является абстрактным или без общедоступного конструктора по умолчанию, вы можете сказать, что X x = (Y)null является допустимым, если и только если x.getClass().isAssignableFrom(Y.class) истинно. - person Earth Engine; 26.07.2013
comment
Почему в этом примере используется b.getClass (). IsAssignableFrom (A.class)? Я думаю, что пример должен быть обратным A.class.isAssignableFrom (b.getClass ()). - person loshad vtapkah; 05.12.2014

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

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

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

person S. Ali Tokmen    schedule 10.12.2010
comment
-1, неверно: null instanceof X (где X - некоторый класс, известный во время компиляции) всегда будет возвращать false. - person Caspar; 04.07.2011
comment
@Caspar, хотя вы правы, основная идея была хорошей. Я отредактировал пост, чтобы он был правильным. - person erickson; 01.11.2011
comment
это полезно, крайний случай всегда важен :). - person trillions; 14.08.2012
comment
Чтобы быть эквивалентной первой строке, вторая строка должна быть X.class.isAssignableFrom(null.getClass()), не так ли? Но да, вызов getClass() по нулевой ссылке приведет к NPE. - person William Price; 05.01.2016
comment
Этот ответ упускает суть - разыменование null не имеет значения, потому что сбой происходит вне операции (вам всегда нужно проверять значение null, прежде чем использовать такую ​​ссылку). В общем, getClass() не следует использовать с isAssignableFrom в первую очередь - операция предназначена для ситуации, когда нет объектов. Если у вас есть ссылка на объект a, используйте a instanceof SomeClass (если вам известен тип SomeClass) или someObject.getClass().isInstance(a) (если вы не знаете тип someObject). - person AndrewF; 03.04.2020

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

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

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

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

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

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

person Owen    schedule 08.12.2011

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

//Define Class< T > type ... 

Object e = new Object();

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

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

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

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
person James Drinkard    schedule 09.09.2013

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

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

OR

... obj instanceof A ...

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

person algebra    schedule 18.03.2010
comment
Это неправда. См. Комментарий Адама Розенфилда stackoverflow.com/questions/496928/ - person Maxim Veksler; 10.09.2010
comment
Не могли бы вы пояснить, что это неправда? Комментарий, на который вы ссылаетесь, не имеет ничего общего со сценарием в моем сообщении. У меня есть тестовый код, подтверждающий мое объяснение. - person algebra; 10.09.2010
comment
Если у вас есть ненулевая ссылка на экземпляр объекта (obj в этом примере) любого типа, вы можете вызвать для него общедоступный метод getClass(), чтобы получить метаданные отражения для реализующего класса. Это верно, даже если этот реализующий тип класса не будет юридически видим в этом месте во время компиляции. Это нормально во время выполнения, потому что для вас, чтобы держать ссылку obj, некоторый путь кода, который в конечном итоге имел имел законный доступ к классу, который создал его и передал (просочился?) Его вам. - person William Price; 05.01.2016
comment
В этом ответе содержится список аналога для obj instanceof A. Это было бы A.class.isInstance(obj). Вы должны использовать isAssignableFrom только в том случае, если у вас есть два класса и ноль объектов. - person AndrewF; 06.01.2021

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 и работает для многих примеров программ, поэтому, хотя я не могу гарантировать, что он захватывает все аспекты isAssignableFrom, он не отключен полностью.

person Stephan Korsholm    schedule 18.03.2016
comment
Пожалуйста, объясните, что делает этот код и как он отвечает на вопрос. - person Fund Monica's Lawsuit; 19.03.2016

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

person Yura    schedule 20.08.2017

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

@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));
}
person Sagan    schedule 13.04.2020

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

person Milan    schedule 24.08.2010
comment
Хм, если у вас есть узкое место в instanceof, я думаю, у вас серьезные проблемы с дизайном ... - person sleske; 27.05.2011
comment
Ответ JBE представляет собой гипотезу, которая отличается от вашей гипотезы. - person FirstName LastName; 15.05.2014