次のうちどれが良いですか?
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
パフォーマンスの面で話す:
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();
...
}
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
警告
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
がnull
でもある場合、これは機能します(falseを返します)。
上記の基本的な違いとは別に、Classのinstanceof演算子とisAssignableFromメソッドの間には、コアとなるわずかな違いがあります。
instanceof
を「これ(左側の部分)thisまたはこのサブクラスのインスタンス(右側の部分)である」と読み、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.
他にも違いがあります。
xがnullであれば、Xのnullインスタンスはfalse
です。
null.getClass()。isAssignableFrom(X)はNullPointerExceptionをスローします。
さらに別の違いがあります。テストするタイプ(Class)が動的な場合、メソッドのパラメータとして渡されると、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
を使用することで、あるクラスの参照が別のインスタンスを取得できるかどうかを判断するための唯一の(おそらく唯一の方法ではないがおそらく最も簡単な)方法を見つけることができました。
したがって、クラスの1つからインスタンスを作成することを考えない限り、私がすべてクラスであったときに代入可能性を比較するためにinstanceof
演算子を使用するのは良い考えではありませんでした。私はこれがずさんだと思いました。
次のような状況を検討してください。型Aがobj型のスーパークラスかどうかを調べたいとします。
... A.class.isAssignableFrom(obj.getClass())...
OR
... obj instanceof A ...
しかしisAssignableFromソリューションでは、objのタイプがここに表示されることが必要です。そうでない場合(例えば、objのタイプがプライベート内部クラスの場合がある)、このオプションは無効です。ただし、instanceofソリューションは常に機能します。
instanceofは、プリミティブ型またはジェネリック型とも使用できません。次のコードのように:
//Define Class< T > type ...
Object e = new Object();
if(e instanceof T) {
// Do something.
}
エラーは次のとおりです。型パラメータTに対してinstanceofチェックを実行できません。実行時にさらに一般型情報が消去されるため、代わりに消去オブジェクトを使用してください。
型の消去によりランタイム参照が削除されたためコンパイルされません。ただし、以下のコードはコンパイルされます。
if( type.isAssignableFrom(e.getClass())){
// Do something.
}
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
つまり、instanceofはisInstance()と同じくらい速く終了できます。 isAssignableFrom()それほど遠くない(+ 0.9%実行時間)。だから、あなたが選んだものは何でも本当の違いはありません