[java] instanceof와 Class.isAssignableFrom (…)의 차이점은 무엇입니까?

다음 중 어느 것이 더 낫습니까?

a instanceof B

또는

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

내가 아는 유일한 차이점은 ‘a’가 null이면 첫 번째는 false를 반환하고 두 번째는 예외를 throw합니다. 그 외에는 항상 동일한 결과를 제공합니까?



답변

를 사용할 때 컴파일 타임에 instanceof클래스를 알아야합니다 B. 사용 isAssignableFrom()하면 동적이며 런타임 중에 변경 될 수 있습니다.


답변

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


답변

성능 측면에서 말하기 :

TL; DR

성능이 비슷한 isInstance 또는 instanceof 를 사용하십시오 . isAssignableFrom 이 약간 느립니다.

성능별로 정렬 :

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

20 회의 예열 반복이있는 JAVA 8 Windows x64에서 2000 회 반복 벤치 마크를 기반으로합니다.

이론에 의하면

부드러운 바이트 코드 뷰어를 사용하여 각 연산자를 바이트 코드로 변환 할 수 있습니다.

문맥 상에:

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

각 연산자가 사용하는 바이트 코드 명령 수를 측정하면 instanceofisInstanceisAssignableFrom 보다 빠를 것으로 예상 할 수 있습니다 . 그러나 실제 성능은 바이트 코드가 아니라 기계 코드 (플랫폼에 따라 다름)에 의해 결정됩니다. 각 사업자에 대한 마이크로 벤치 마크를 해보자.

벤치 마크

크레딧 : @ 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 및 플랫폼에 따라 다릅니다. 각 작업간에 큰 차이가 없으므로 Solaris, Mac 또는 Linux와 같은 다른 JAVA 버전 및 / 또는 플랫폼에서 다른 결과 (및 다른 순서)를 얻을 수 있습니다.
  • 벤치 마크는 “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

노트

원래이 게시물은 원시 JAVA에서 for 루프를 사용하여 자체 벤치 마크를 수행했으며 Just In Time과 같은 일부 최적화로 루프를 제거 할 수 있으므로 신뢰할 수없는 결과를 얻었습니다. 따라서 대부분 JIT 컴파일러가 루프를 최적화하는 데 걸린 시간을 측정 했습니다. 자세한 내용 은 반복 횟수와 무관 한 성능 테스트 를 참조하십시오.

관련 질문


답변

보다 직접적인 a instanceof B것은

B.class.isInstance(a)

이 작품 (false를 반환)의 때 a입니다 null도.


답변

위에서 언급 한 기본적인 차이점 외에, class에서 instanceof 연산자와 isAssignableFrom 메소드 사이에는 핵심적인 미묘한 차이가 있습니다.

읽기 instanceof“이 (왼쪽)는 이것의 인스턴스 또는 이것의 서브 클래스 (오른쪽 부분)”로 읽을 x.getClass().isAssignableFrom(Y.class)“수 있습니까 쓰기로 X x = new Y()“. 즉, instanceof 연산자는 왼쪽 객체가 오른쪽 클래스의 서브 클래스와 같은지 또는 서브 클래스 isAssignableFrom인지 확인하는 한편 , 매개 변수 클래스 (from)의 객체를 메소드가 호출 된 클래스의 참조에 할당 할 수 있는지 확인합니다.
이 두 가지 모두 참조 유형이 아닌 실제 인스턴스를 고려합니다.

C가 B를 확장하고 B가 A를 확장하는 3 개의 클래스 A, B 및 C의 예를 고려하십시오.

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.


답변

또 다른 차이점이 있습니다.

X의 null 인스턴스는 false 무엇이든 상관없이

null.getClass (). isAssignableFrom (X)는 NullPointerException을 발생시킵니다.


답변

또 다른 차이점이 있습니다. 테스트 할 유형 (클래스)이 동적 인 경우 (예 : 메소드 매개 변수로 전달 된 경우) 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
}

죄송합니다.이 답변은 이미 다룹니다. 이 예는 누군가에게 도움이 될 수 있습니다.