instanceofとClass.isAssignableFrom(…)の違いは何ですか?


458

次のうちどれが良いですか?

a instanceof B

または

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

私が知っている唯一の違いは、「a」がnullの場合、最初のものはfalseを返し、2番目のものは例外をスローすることです。それ以外は常に同じ結果になりますか?


17
レコードの場合、でisinstance()は、オブジェクトがクラス型にキャストすることが可能かどうかを確認するための最も便利な方法である(詳細については、以下を参照してください。tshikatshikaaa.blogspot.nl/2012/07/...を
ジェローム・Verstrynge

回答:


498

を使用する場合、コンパイル時にinstanceofのクラスを知る必要がありますB。使用isAssignableFrom()する場合、動的で実行時に変更できます。


12
わかりません-書き込めない理由を詳しく説明してくださいa instanceof Bref.getClass()。これは、説明がほとんどない(またはその欠如)ので、どのように受け入れられた回答になるのでしょうか。
Eliran Malka

65
構文はa instanceof Brefそうではありませんa instanceof Bref.class。instanceof演算子の2番目の引数はクラス名であり、クラスオブジェクトインスタンスに解決される式ではありません。
Brandon Bloom

2
はい、「ダイナミック」は言うまでもありません:)パフォーマンス以外は、これは本当の違いです。
peterk 2013年

2
@EliranMalka多分あなたはランタイムで生成されたクラスを持つことができます。プロキシオブジェクトのように。
土屋ワーグナー2016年

したがって、では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を使用する意味がわかりません。
ジミーT.

116

パフォーマンスに関して話す:

TL; DR

同様のパフォーマンスを持つisInstanceまたはinstanceofを使用しますisAssignableFromは少し遅いです。

パフォーマンスで並べ替え:

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

JAVA 8 Windows x64での2000回の反復のベンチマークに基づき、20回のウォームアップ反復。

理論的には

バイトコードビューアのようなソフトを使用して、各演算子をバイトコードに変換できます。

次のコンテキストで:

package foo;

public class Benchmark
{
  public static final Object a = new A();
  public static final Object b = new B();

  ...

}

JAVA:

b instanceof A;

バイトコード:

getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A

JAVA:

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

JAVA:

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とプラットフォームに依存します。各操作に大きな違いはないため、異なるJAVAバージョンやSolaris、Mac、Linuxなどのプラットフォームで異なる結果(そしておそらく異なる順序!)が得られる可能性があります。
  • ベンチマークは、「BがAを拡張する」場合の「BがAのインスタンスである」のパフォーマンスを直接比較します。クラス階層がより深く、より複雑な場合(BがXを拡張し、Yを拡張し、Zを拡張し、Aを拡張するなど)、結果は異なる場合があります。
  • 通常、最初に演算子(最も便利)の1つを選択してコードを記述し、次にコードをプロファイルしてパフォーマンスのボトルネックがないかどうかを確認することをお勧めします。多分この演算子はあなたのコードの文脈では無視できるか、多分...
  • 前のポイントに関連して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

注意

もともとこの投稿はraw JAVAでforループを使用して独自のベンチマークを行っていましたが、ジャストインタイムなどの最適化によってループを排除できるため、信頼性の低い結果が得られました。したがって、これは主に、JITコンパイラーがループを最適化するのにかかった時間を測定するものでした。詳細については、反復回数に依存しないパフォーマンステストを参照してください。

関連する質問


6
うん、(キャストの背後にあるバイトコード)instanceofと基本的に同じロジックを使用するバイトコードですcheckcast。JITCの最適化の程度に関係なく、他のオプションよりも本質的に高速です。
Hot Licks 2013

1
isAssignableFrom()動的であるので、これは理にかなっています。
Matthieu

はい、JMHの結果は完全に異なります(すべての速度が同じです)。
Yura

こんにちは、素晴らしいベンチマークです。isAssignableFromが何千回も呼び出され、instanceofに変更すると本当に違いが出る状況に遭遇しました。この返信は、ブログ投稿のどこかに価値があります...;)
Martin

33

より直接的な等価物a instanceof B

B.class.isInstance(a)

これは、作品(falseを返す)aですnullあまりにも。


かっこいいですが、これは質問の答えにはならず、コメントでした。
Madbreaks

23

上記の基本的な違いとは別に、インスタンスの演算子とクラスのisAssignableFromメソッドの間には、重要な微妙な違いがあります。

読むinstanceof「この(左部分)は、こののインスタンスまたはその任意のサブクラス(右側)である」と読みとしてx.getClass().isAssignableFrom(Y.class)「缶Iの書き込みとして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 AA.class.isAssignableFrom(b.getClass())(OPが気づいたように)と同等です。あなたの例は正しいですが無関係です。
Karu

が抽象である場合、またはデフォルトのパブリックコンストラクタnew Y()がない場合Yは合法ではない可能性があるため、trueのX x = (Y)null場合にのみ合法であると言えx.getClass().isAssignableFrom(Y.class)ます。
Earth Engine

2
この例で「b.getClass()。isAssignableFrom(A.class)」を使用する理由 例は逆のA.class.isAssignableFrom(b.getClass())であるべきだと思います。
loshad vtapkah 2014

14

また、別の違いもあります。

Xがfalse何であっても、Xのnull instanceof は

null.getClass()。isAssignableFrom(X)はNullPointerExceptionをスローします


4
-1、不正解:null instanceof X(Xはコンパイル時に認識されるクラスです)は常にを返しfalseます。
カスパー、2011

4
@Casparあなたが正しい間、基本的なアイデアは良い点でした。投稿が正しいように編集しました。
エリクソン2011年

1
これは役に立ちます。大文字と小文字の区別は常に重要です:)。

最初の行と同等にするために、2番目の行はそうである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のw /クラス、Class.isInstanceは'instanceof'に
bestsss

@bestsssの正しいコメントを具体的なコードに入れるには:オブジェクト(this)があるのでclazz.isInstance(this)、例ではより良いでしょう。
AndrewF

7

このスレッドは、instanceofとの違いについての洞察を私に提供してくれたisAssignableFromので、自分の何かを共有したいと思いました。

私が使用していることを発見したisAssignableFrom1クラスの参照が1の比較を行うためにどちらのクラスのインスタンスを持っている場合、別のインスタンスを取ることができます場合は、1つの自己を依頼するだけ(おそらくだけではなく、おそらく最も簡単な)方法であること。

したがって、instanceofクラスの1つからインスタンスを作成することを考えていなかった場合を除き、演算子を使用して代入可能性を比較することは、すべてがクラスである場合には適切だとは思いませんでした。これはだらしないだろうと思った。


5

instanceofは、プリミティブ型またはジェネリック型でも使用できません。次のコードのように:

//Define Class< T > type ... 

Object e = new Object();

if(e instanceof T) {
  // Do something.
}

エラーは次のとおりです:型パラメーターTに対してinstanceofチェックを実行できません。実行時にさらに一般的な型情報が消去されるため、代わりに消去オブジェクトを使用してください。

型の消去によりランタイム参照が削除されるため、コンパイルされません。ただし、以下のコードはコンパイルされます。

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}

4

以下の状況を検討してください。タイプAがobjのタイプのスーパークラスであるかどうかを確認したい場合は、

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

または

... obj instanceof A ...

しかし、isAssignableFromソリューションでは、objのタイプがここに表示される必要があります。そうでない場合(たとえば、objの型がプライベート内部クラスである可能性がある)、このオプションは無効です。ただし、instanceofソリューションは常に機能します。


2
それは真実ではありません。「アダムローゼンフィールド」のコメントを参照してくださいstackoverflow.com/questions/496928/...
マキシムVeksler

1
「それは本当ではない」について詳しく説明していただけませんか?あなたが参照するコメントは私の投稿のシナリオとは関係ありません。私の説明を裏付けるいくつかのテストコードがあります。
代数学

任意のタイプのオブジェクトインスタンス(objこの例では)へのnull以外の参照がある場合は、そのインスタンスのパブリックメソッドを呼び出して、実装クラスのリフレクションメタデータを取得できます。これは、その実装クラス型がコンパイル時にその場所で合法的に見えない場合でも当てはまります。実行時にそれのOKはあなたが保持するため、ので参照し、最終的にいくつかのコードパスしなかったクラスへの法的なアクセス権を持つには、1を作成した(漏れた?)それをあなたに。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

つまり、instanceofisInstance()およびisAssignableFrom()同じくらい速く(実行時間+ 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
ええと

1
JBEの答えは、あなたの仮説とは異なる仮説を提示します。
Alastorムーディー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.