次のうちどれが良いですか?
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()
する場合、動的で実行時に変更できます。
a instanceof Bref.getClass()
。これは、説明がほとんどない(またはその欠如)ので、どのように受け入れられた回答になるのでしょうか。
a instanceof Bref
そうではありませんa instanceof Bref.class
。instanceof演算子の2番目の引数はクラス名であり、クラスオブジェクトインスタンスに解決される式ではありません。
B.class.isAssignableFrom(a.getClass())
、Bは既知であり、a instanceof B
より優れています。正しい?
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();
...
}
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のおかげで、コードはある時点で最適化され、次のようになります。
注意
もともとこの投稿はraw JAVAでforループを使用して独自のベンチマークを行っていましたが、ジャストインタイムなどの最適化によってループを排除できるため、信頼性の低い結果が得られました。したがって、これは主に、JITコンパイラーがループを最適化するのにかかった時間を測定するものでした。詳細については、反復回数に依存しないパフォーマンステストを参照してください。
関連する質問
instanceof
と基本的に同じロジックを使用するバイトコードですcheckcast
。JITCの最適化の程度に関係なく、他のオプションよりも本質的に高速です。
isAssignableFrom()
動的であるので、これは理にかなっています。
より直接的な等価物a instanceof B
は
B.class.isInstance(a)
これは、作品(falseを返す)a
ですnull
あまりにも。
上記の基本的な違いとは別に、インスタンスの演算子とクラスの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.
b instanceof A
A.class.isAssignableFrom(b.getClass())
(OPが気づいたように)と同等です。あなたの例は正しいですが無関係です。
new Y()
がない場合Y
は合法ではない可能性があるため、trueのX x = (Y)null
場合にのみ合法であると言えx.getClass().isAssignableFrom(Y.class)
ます。
また、別の違いもあります。
Xがfalse
何であっても、Xのnull instanceof は
null.getClass()。isAssignableFrom(X)はNullPointerExceptionをスローします
null instanceof X
(Xはコンパイル時に認識されるクラスです)は常にを返しfalse
ます。
X.class.isAssignableFrom(null.getClass())
べきではありませんか?しかし、はい、getClass()
null参照を呼び出すとNPEになります。
getClass()
使用しないでください。isAssignableFrom
この操作は、オブジェクトがない状況を対象としています。あなたは、オブジェクト参照がある場合はa
、使用をa instanceof SomeClass
(あなたがあればやるタイプを知っているSomeClass
)、またはsomeObject.getClass().isInstance(a)
(あなたがいる場合していないの種類を知っていますsomeObject
)。
さらに別の違いがあります。テストするタイプ(クラス)が動的である場合、たとえばメソッドパラメータとして渡された場合、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
}
おっと、この回答はすでにカバーされています。多分この例は誰かに役立つでしょう。
this
)があるのでclazz.isInstance(this)
、例ではより良いでしょう。
このスレッドは、instanceof
との違いについての洞察を私に提供してくれたisAssignableFrom
ので、自分の何かを共有したいと思いました。
私が使用していることを発見したisAssignableFrom
1クラスの参照が1の比較を行うためにどちらのクラスのインスタンスを持っている場合、別のインスタンスを取ることができます場合は、1つの自己を依頼するだけ(おそらくだけではなく、おそらく最も簡単な)方法であること。
したがって、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())...
または
... obj instanceof A ...
しかし、isAssignableFromソリューションでは、objのタイプがここに表示される必要があります。そうでない場合(たとえば、objの型がプライベート内部クラスである可能性がある)、このオプションは無効です。ただし、instanceofソリューションは常に機能します。
obj
この例では)へのnull以外の参照がある場合は、そのインスタンスのパブリックメソッドを呼び出して、実装クラスのリフレクションメタデータを取得できます。これは、その実装クラス型がコンパイル時にその場所で合法的に見えない場合でも当てはまります。実行時にそれのOKはあなたが保持するため、ので参照し、最終的にいくつかのコードパスしなかったクラスへの法的なアクセス権を持つには、1を作成した(漏れた?)それをあなたに。getClass()
obj
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%)離れていません。だから何を選んでも本当の違いはない
実際にそれを示すいくつかの例はどうですか...
@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));
}
私たちのチームで行ったいくつかのテストは、それA.class.isAssignableFrom(B.getClass())
よりも速く機能することを示していますB instanceof A
。これは、多数の要素でこれをチェックする必要がある場合に非常に役立ちます。
instanceof