485

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

a instanceof B

また

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

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

4

14 に答える 14

531

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

于 2009-01-30T19:58:18.790 に答える
213

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

于 2009-01-30T19:53:16.633 に答える
119

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

TL;DR

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

パフォーマンス順:

  1. isInstance
  2. インスタンス(+ 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();

  ...

}

ジャワ:

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

各オペレーターが使用するバイトコード命令の数を測定すると、 instanceofisInstanceが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 のおかげで、コードはある時点で最適化され、次のようになります。

  • インスタンス: 6ms
  • isInstance: 12ms
  • isAssignableFrom: 15ms

ノート

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

関連する質問

于 2012-08-24T09:21:49.037 に答える
34

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

B.class.isInstance(a)

aあまりにもある場合、これは機能します (false を返します) null

于 2011-08-15T20:58:53.560 に答える
29

上記の基本的な違いとは別に、クラスの 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.
于 2010-09-07T10:51:30.387 に答える
14

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

null instanceof X は X がfalse何であれ

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

于 2010-12-10T14:37:16.327 に答える
13

さらに別の違いがあります。テスト対象の型 (クラス) が動的である場合、たとえばメソッド パラメーターとして渡される場合、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
}

おっと、この答えはすでにカバーされているようです。たぶん、この例は誰かに役立つでしょう。

于 2011-01-25T16:16:10.970 に答える
7

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

あるisAssignableFromクラスの参照が別のクラスのインスタンスを取得できるかどうかを自問する唯一の (おそらく唯一ではないかもしれませんが、おそらく最も簡単な) 方法は、どちらのクラスのインスタンスも比較に使用できない場合です。

したがって、instanceofクラスの 1 つからインスタンスを作成することを考えていない限り、演算子を使用して割り当て可能性を比較することは良い考えではありませんでした。これはだめだろうと思いました。

于 2011-12-08T16:04:19.213 に答える
6

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

//Define Class< T > type ... 

Object e = new Object();

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

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

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

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
于 2013-09-09T19:30:32.433 に答える
3

次の状況を検討してください。タイプ A が obj のタイプのスーパークラスであるかどうかを確認したい場合、次のいずれかを実行できます。

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

また

... A の obj インスタンス ...

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

于 2010-03-18T20:03:58.073 に答える
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 のすべての側面をキャプチャすることは保証できませんが、完全にオフというわけではありません。

于 2016-03-18T13:59:30.137 に答える
-2

私たちのチームで行ったいくつかのテストではA.class.isAssignableFrom(B.getClass())B instanceof A. これは、多数の要素でこれをチェックする必要がある場合に非常に役立ちます。

于 2010-08-24T22:05:49.943 に答える