다음 중 어느 것이 더 낫습니까?
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
하면 심각한 디자인 문제가 있다고 생각합니다 ...