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


457

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

a instanceof B

또는

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

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


17
레코드의 경우 isInstance ()는 객체를 클래스 유형으로 캐스트 할 수 있는지 확인하는 가장 편리한 방법입니다 (자세한 내용은 tshikatshikaaa.blogspot.nl/2012/07/… 참조 )
Jérôme Verstrynge

답변:


497

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


12
나는 그것을 얻지 못한다- 우리가 쓸 수 없는지에 대해 자세히 설명해주세요 a instanceof Bref.getClass(). 작은 설명이나 그 부족으로 어떻게 이것이 받아 들여질 수 있는가?
Eliran Malka

65
구문이 a instanceof Bref아닙니다 a instanceof Bref.class. instanceof 연산자의 두 번째 인수는 클래스 이름이며 클래스 객체 인스턴스로 해석되는 표현식이 아닙니다.
Brandon Bloom

2
예, "동적"은 말할 것도없이 진행됩니다. 성능 외에, 이것은 진정한 차이입니다.
peterk 2016 년

2
@EliranMalka 어쩌면 런타임에 생성 된 클래스를 가질 수 있습니다. 프록시 객체처럼.
바그너 츠치야

에서 B.class.isAssignableFrom(a.getClass())B는 알려져 있으며 a instanceof B더 좋습니다. 권리?
Florian F

208

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


10
기본 유형으로 instanceof / isAssignableFrom을 사용하는 시점을 알 수 없습니다.
Jimmy T.

116

성능 측면에서 말하기 :

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 컴파일러가 루프를 최적화하는 데 걸린 시간을 측정 했습니다. 자세한 내용 은 반복 횟수와 무관 한 성능 테스트 를 참조하십시오.

관련 질문


6
그렇습니다 . (캐스팅 뒤에 바이트 코드) instanceof와 본질적으로 동일한 논리를 사용하는 바이트 코드입니다 checkcast. JITC 최적화 수준에 관계없이 다른 옵션보다 본질적으로 빠릅니다.
핫 릭

1
isAssignableFrom()동적 이기 때문에 말이됩니다 .
Matthieu

그렇습니다 .JMH 결과는 완전히 다릅니다 (모두 같은 속도).
Yura

안녕하세요, 좋은 벤치 마크는 isAssignableFrom이 수천 번 호출 된 상황에서 실제로 인스턴스로 변경되어 실제로 차이가 생겼습니다. 이 답변은 블로그 게시물의 가치가있을 것입니다 ...;)
Martin

33

보다 직접적인 a instanceof B것은

B.class.isInstance(a)

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


멋지지만 이것은 질문에 대답하지 않으며 주석이어야합니다.
Madbreaks

23

위에서 언급 한 기본적인 차이점 외에, 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.

3
b instanceof A에 해당 A.class.isAssignableFrom(b.getClass())합니다 (OP 눈치로). 귀하의 예는 정확하지만 관련이 없습니다.
Karu

때문에 new Y()경우 법적하지 않을 수 있습니다 Y추상적 인 또는 공용 기본 생성자없이, 당신은 말할 수있는 X x = (Y)null경우에만 경우 법적 x.getClass().isAssignableFrom(Y.class)사실이다.
어스 엔진

2
이 예제에서 왜 'b.getClass (). isAssignableFrom (A.class)'입니까? 예제는 역 A.class.isAssignableFrom (b.getClass ())이어야한다고 생각합니다.
loshad vtapkah

14

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

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

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


4
-1, 부정확 함 : null instanceof X(여기서 X는 컴파일 타임에 알려진 클래스 임) 항상 반환 false합니다.
Caspar

4
@Caspar는 정확하지만 기본 아이디어는 좋은 지적이었습니다. 게시물이 올바른지 편집했습니다.
erickson

1
이것은 도움이되며, 대소 문자는 항상 중요합니다 :).
조조

첫 번째 줄과 동일하려면 두 번째 줄이 X.class.isAssignableFrom(null.getClass())아니어야합니까? 그러나 예, getClass()null 참조를 호출 하면 NPE가 발생합니다.
윌리엄 가격

이 답변은 요점을 놓치게됩니다. null 역 참조는 실패가 작업 외부에서 발생하기 때문에 관련이 없습니다 (그러한 참조를 사용하기 전에 항상 null을 확인해야합니다). 일반적으로 처음에는 getClass()함께 사용해서는 안됩니다 isAssignableFrom. 작업은 객체가없는 상황을위한 것입니다. 당신은 객체 참조가있는 경우 a, 사용을 a instanceof SomeClass(당신이 경우에 유형을 알고 SomeClass) 또는 someObject.getClass().isInstance(a)(당신이 경우 하지 의 유형을 알고있다 someObject).
AndrewF

12

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

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


3
실제로 대답이 실제로 정확하지 않음 isAssignableFrom wclass 클래스에서, Class.isInstance는 'instanceof'
bestsss

@bestsss의 정확한 주석을 구체적인 코드에 넣으려면 : 객체 ( this) clazz.isInstance(this)가 있으므로 예제에서 더 좋습니다.
AndrewF

7

이 스레드 instanceof는와 다른 점에 대한 통찰력을 제공 isAssignableFrom했기 때문에 내 자신의 것을 공유 할 것이라고 생각했습니다.

나는 isAssignableFrom한 클래스의 참조가 다른 클래스의 인스턴스를 가지고 비교를 수행 할 수없는 인스턴스가있을 때 하나의 클래스에 대한 참조가 다른 클래스의 인스턴스를 취할 수 있는지 여부를 스스로에게 묻는 유일한 방법 일 것입니다.

따라서 instanceof클래스 중 하나에서 인스턴스를 만드는 것을 고려하지 않는 한 클래스를 사용할 때 할당 가능성을 좋은 아이디어로 비교하기 위해 연산자를 사용하는 것을 찾지 못했습니다 . 나는 이것이 조잡하다고 생각했다.


5

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

4

다음 상황을 고려하십시오. 유형 A가 obj 유형의 수퍼 클래스인지 확인하려는 경우 다음 중 하나를 수행 할 수 있습니다.

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

또는

... A의 obj 인스턴스 ...

그러나 isAssignableFrom 솔루션을 사용하려면 여기에서 obj 유형을 볼 수 있어야합니다. 그렇지 않은 경우 (예 : obj 유형이 개인 내부 클래스 일 수 있음)이 옵션은 사용되지 않습니다. 그러나 instanceof 솔루션은 항상 작동합니다.


2
사실이 아닙니다. "Adam Rosenfield"설명 stackoverflow.com/questions/496928/…
Maxim Veksler

1
"그게 사실이 아니야"를 정교하게 설명해 주시겠습니까? 당신이 말하는 의견은 내 게시물의 시나리오와 관련이 없습니다. 내 설명을 뒷받침하는 테스트 코드가 있습니다.
대수

모든 유형 의 객체 인스턴스 ( obj이 예제에서)에 대해 null이 아닌 참조가있는 경우 해당 객체 에서 public getClass()메소드를 호출 하여 구현 클래스에 대한 리플렉션 메타 데이터를 얻을 수 있습니다. 컴파일 타임에 해당 위치에서 클래스 유형을 구현하는 것이 합법적으로 보이지 않는 경우에도 마찬가지입니다. 런타임에의 확인 당신이 개최하기 때문에 obj참조를, 궁극적으로는 일부 코드 경로 했던 클래스에 대한 법적 액세스 할 수 있습니다 하나를 작성했다 (유출?)을 당신에게.
윌리엄 가격

0
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
이 코드의 기능과 질문에 어떻게 대답하는지 설명하십시오.
기금 모니카의 소송

0

성능 측면에서 말하기 "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 % 실행 시간)라는 결론을 내릴 수 있습니다 . 그래서 당신이 선택한 것이 무엇이든 실제 차이가 없습니다.


0

실제로 보여주는 몇 가지 예는 어떻습니까?

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

-2

팀에서 수행 한 일부 테스트에서 A.class.isAssignableFrom(B.getClass())보다 빠르게 작동하는 것으로 나타났습니다 B instanceof A. 많은 수의 요소에서이를 확인해야하는 경우 매우 유용 할 수 있습니다.


13
흠,에 병목 현상이 발생 instanceof하면 심각한 디자인 문제가 있다고 생각합니다 ...
sleske

1
JBE의 대답은 가설과 다른 가설을 제시합니다.
Alastor Moody 16:15에
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.