다음 중 어느 것이 더 낫습니까?
a instanceof B
또는
B.class.isAssignableFrom(a.getClass())
내가 아는 유일한 차이점은 'a'가 null이면 첫 번째는 false를 반환하고 두 번째는 예외를 throw합니다. 그 외에는 항상 동일한 결과를 제공합니까?
다음 중 어느 것이 더 낫습니까?
a instanceof B
또는
B.class.isAssignableFrom(a.getClass())
내가 아는 유일한 차이점은 'a'가 null이면 첫 번째는 false를 반환하고 두 번째는 예외를 throw합니다. 그 외에는 항상 동일한 결과를 제공합니까?
답변:
를 사용할 때 컴파일 타임에 instanceof클래스를 알아야합니다 B. 사용 isAssignableFrom()하면 동적이며 런타임 중에 변경 될 수 있습니다.
a instanceof Bref.getClass(). 작은 설명이나 그 부족으로 어떻게 이것이 받아 들여질 수 있는가?
a instanceof Bref아닙니다 a instanceof Bref.class. instanceof 연산자의 두 번째 인수는 클래스 이름이며 클래스 객체 인스턴스로 해석되는 표현식이 아닙니다.
B.class.isAssignableFrom(a.getClass())B는 알려져 있으며 a instanceof B더 좋습니다. 권리?
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 이 약간 느립니다.
성능별로 정렬 :
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
경고
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 덕분에 코드는 어느 시점에서 최적화되었으며 다음과 같은 이점이 있습니다.
노트
원래이 게시물은 원시 JAVA에서 for 루프를 사용하여 자체 벤치 마크를 수행했으며 Just In Time과 같은 일부 최적화로 루프를 제거 할 수 있으므로 신뢰할 수없는 결과를 얻었습니다. 따라서 대부분 JIT 컴파일러가 루프를 최적화하는 데 걸린 시간을 측정 했습니다. 자세한 내용 은 반복 횟수와 무관 한 성능 테스트 를 참조하십시오.
관련 질문
instanceof와 본질적으로 동일한 논리를 사용하는 바이트 코드입니다 checkcast. JITC 최적화 수준에 관계없이 다른 옵션보다 본질적으로 빠릅니다.
isAssignableFrom()동적 이기 때문에 말이됩니다 .
위에서 언급 한 기본적인 차이점 외에, 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.
b instanceof A에 해당 A.class.isAssignableFrom(b.getClass())합니다 (OP 눈치로). 귀하의 예는 정확하지만 관련이 없습니다.
new Y()경우 법적하지 않을 수 있습니다 Y추상적 인 또는 공용 기본 생성자없이, 당신은 말할 수있는 X x = (Y)null경우에만 경우 법적 x.getClass().isAssignableFrom(Y.class)사실이다.
또 다른 차이점이 있습니다.
X의 null 인스턴스는 false 무엇이든 상관없이
null.getClass (). isAssignableFrom (X)는 NullPointerException을 발생시킵니다.
null instanceof X(여기서 X는 컴파일 타임에 알려진 클래스 임) 항상 반환 false합니다.
X.class.isAssignableFrom(null.getClass())아니어야합니까? 그러나 예, getClass()null 참조를 호출 하면 NPE가 발생합니다.
getClass()함께 사용해서는 안됩니다 isAssignableFrom. 작업은 객체가없는 상황을위한 것입니다. 당신은 객체 참조가있는 경우 a, 사용을 a instanceof SomeClass(당신이 경우에 할 유형을 알고 SomeClass) 또는 someObject.getClass().isInstance(a)(당신이 경우 하지 의 유형을 알고있다 someObject).
또 다른 차이점이 있습니다. 테스트 할 유형 (클래스)이 동적 인 경우 (예 : 메소드 매개 변수로 전달 된 경우) 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
}
죄송합니다.이 답변은 이미 다룹니다. 이 예는 누군가에게 도움이 될 수 있습니다.
this) clazz.isInstance(this)가 있으므로 예제에서 더 좋습니다.
이 스레드 instanceof는와 다른 점에 대한 통찰력을 제공 isAssignableFrom했기 때문에 내 자신의 것을 공유 할 것이라고 생각했습니다.
나는 isAssignableFrom한 클래스의 참조가 다른 클래스의 인스턴스를 가지고 비교를 수행 할 수없는 인스턴스가있을 때 하나의 클래스에 대한 참조가 다른 클래스의 인스턴스를 취할 수 있는지 여부를 스스로에게 묻는 유일한 방법 일 것입니다.
따라서 instanceof클래스 중 하나에서 인스턴스를 만드는 것을 고려하지 않는 한 클래스를 사용할 때 할당 가능성을 좋은 아이디어로 비교하기 위해 연산자를 사용하는 것을 찾지 못했습니다 . 나는 이것이 조잡하다고 생각했다.
instanceof는 기본 유형 또는 일반 유형과 함께 사용할 수 없습니다. 다음 코드와 같이
//Define Class< T > type ...
Object e = new Object();
if(e instanceof T) {
// Do something.
}
오류는 다음과 같습니다. 유형 매개 변수 T에 대해 instanceof check를 수행 할 수 없습니다. 런타임에 추가 일반 유형 정보가 지워 지므로 대신 오브젝트 삭제를 사용하십시오.
런타임 참조를 제거하는 유형 삭제로 인해 컴파일되지 않습니다. 그러나 아래 코드는 컴파일됩니다.
if( type.isAssignableFrom(e.getClass())){
// Do something.
}
다음 상황을 고려하십시오. 유형 A가 obj 유형의 수퍼 클래스인지 확인하려는 경우 다음 중 하나를 수행 할 수 있습니다.
... A.class.isAssignableFrom (obj.getClass ()) ...
또는
... A의 obj 인스턴스 ...
그러나 isAssignableFrom 솔루션을 사용하려면 여기에서 obj 유형을 볼 수 있어야합니다. 그렇지 않은 경우 (예 : obj 유형이 개인 내부 클래스 일 수 있음)이 옵션은 사용되지 않습니다. 그러나 instanceof 솔루션은 항상 작동합니다.
obj이 예제에서)에 대해 null이 아닌 참조가있는 경우 해당 객체 에서 public getClass()메소드를 호출 하여 구현 클래스에 대한 리플렉션 메타 데이터를 얻을 수 있습니다. 컴파일 타임에 해당 위치에서 클래스 유형을 구현하는 것이 합법적으로 보이지 않는 경우에도 마찬가지입니다. 런타임에의 확인 당신이 개최하기 때문에 obj참조를, 궁극적으로는 일부 코드 경로 했던 클래스에 대한 법적 액세스 할 수 있습니다 하나를 작성했다 (유출?)을 당신에게.
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"(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 () 만큼 빠르지 않은 instanceof (+ 0.9 % 실행 시간)라는 결론을 내릴 수 있습니다 . 그래서 당신이 선택한 것이 무엇이든 실제 차이가 없습니다.
실제로 보여주는 몇 가지 예는 어떻습니까?
@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));
}
팀에서 수행 한 일부 테스트에서 A.class.isAssignableFrom(B.getClass())보다 빠르게 작동하는 것으로 나타났습니다 B instanceof A. 많은 수의 요소에서이를 확인해야하는 경우 매우 유용 할 수 있습니다.
instanceof하면 심각한 디자인 문제가 있다고 생각합니다 ...