次のうちどれが良いですか?
a instanceof B
また
B.class.isAssignableFrom(a.getClass())
私が知っている唯一の違いは、「a」が null の場合、最初は false を返し、2 番目は例外をスローすることです。それ以外は、常に同じ結果になりますか?
次のうちどれが良いですか?
a instanceof B
また
B.class.isAssignableFrom(a.getClass())
私が知っている唯一の違いは、「a」が null の場合、最初は false を返し、2 番目は例外をスローすることです。それ以外は、常に同じ結果になりますか?
を使用する場合、コンパイル時instanceof
に のクラスを知っている必要があります。B
使用isAssignableFrom()
すると動的になり、実行時に変更されます。
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は少し遅くなります。
パフォーマンス順:
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);
各オペレーターが使用するバイトコード命令の数を測定すると、 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ループを使用して独自のベンチマークを行っていましたが、ジャスト イン タイムなどの最適化によってループを排除できるため、信頼できない結果が得られました。そのため、ほとんどの場合、JIT コンパイラーがループを最適化するのにかかった時間を測定していました。詳細については、反復回数に依存しないパフォーマンス テストを参照してください。
関連する質問
より直接的な等価物a instanceof B
は
B.class.isInstance(a)
a
あまりにもある場合、これは機能します (false を返します) null
。
上記の基本的な違いとは別に、クラスの 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.
また、別の違いがあります。
null instanceof X は X がfalse
何であれ
null.getClass().isAssignableFrom(X) は NullPointerException をスローします
さらに別の違いがあります。テスト対象の型 (クラス) が動的である場合、たとえばメソッド パラメーターとして渡される場合、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
}
おっと、この答えはすでにカバーされているようです。たぶん、この例は誰かに役立つでしょう。
instanceof
このスレッドは、 との違いについての洞察を提供してくれたisAssignableFrom
ので、私自身のことを共有したいと思いました。
あるisAssignableFrom
クラスの参照が別のクラスのインスタンスを取得できるかどうかを自問する唯一の (おそらく唯一ではないかもしれませんが、おそらく最も簡単な) 方法は、どちらのクラスのインスタンスも比較に使用できない場合です。
したがって、instanceof
クラスの 1 つからインスタンスを作成することを考えていない限り、演算子を使用して割り当て可能性を比較することは良い考えではありませんでした。これはだめだろうと思いました。
instanceof は、プリミティブ型またはジェネリック型でも使用できません。次のコードのように:
//Define Class< T > type ...
Object e = new Object();
if(e instanceof T) {
// Do something.
}
エラーは次のとおりです: 型パラメーター T に対して instanceof チェックを実行できません。それ以上のジェネリック型情報は実行時に消去されるため、代わりに消去オブジェクトを使用してください。
型消去によりランタイム参照が削除されるため、コンパイルされません。ただし、以下のコードはコンパイルされます。
if( type.isAssignableFrom(e.getClass())){
// Do something.
}
次の状況を検討してください。タイプ A が obj のタイプのスーパークラスであるかどうかを確認したい場合、次のいずれかを実行できます。
... A.class.isAssignableFrom(obj.getClass()) ...
また
... A の obj インスタンス ...
ただし、isAssignableFrom ソリューションでは、obj の型がここに表示される必要があります。そうでない場合 (たとえば、obj の型がプライベート内部クラスである可能性がある場合)、このオプションは無効です。ただし、instanceof ソリューションは常に機能します。
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 のすべての側面をキャプチャすることは保証できませんが、完全にオフというわけではありません。
私たちのチームで行ったいくつかのテストではA.class.isAssignableFrom(B.getClass())
、B instanceof A
. これは、多数の要素でこれをチェックする必要がある場合に非常に役立ちます。