다음 중 어느 것이 더 낫습니까?
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 이 약간 느립니다.
성능별로 정렬 :
- isInstance
- instanceof (+ 0.5 %)
- 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);
각 연산자가 사용하는 바이트 코드 명령 수를 측정하면 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 및 플랫폼에 따라 다릅니다. 각 작업간에 큰 차이가 없으므로 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
}
죄송합니다.이 답변은 이미 다룹니다. 이 예는 누군가에게 도움이 될 수 있습니다.