Class.isInstanceとClass.isAssignableFromおよびinstanceof
1. 序章
このクイックチュートリアルでは、 instanceof 、 Class.isInstance 、およびClass.isAssignableFromの違いを見ていきます。 それぞれの方法の使い方と、それらの違いについて学びます。
2. 設定
instanceof 、 Class.isInstance 、および Class.isAssignableFrom 機能を調べるときに使用するインターフェイスと、いくつかのクラスを設定しましょう。
まず、インターフェースを定義しましょう。
public interface Shape {
}
次に、Shapeを実装するクラスを定義しましょう。
public class Triangle implements Shape {
}
次に、Triangleを拡張するクラスを作成します。
public class IsoscelesTriangle extends Triangle {
}
3. インスタンスの
instanceof キーワードは二項演算子であり、これを使用して、特定のオブジェクトが特定のタイプのインスタンスであるかどうかを確認できます。 したがって、操作の結果はtrueまたはfalseのいずれかになります。 さらに、 instanceof キーワードは、オブジェクトが別のタイプをサブタイプ化するかどうかを確認するための最も一般的で簡単な方法です。
instanceof演算子でクラスを使用してみましょう。
Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Shape nonspecificShape = null;
assertTrue(shape instanceof Shape);
assertTrue(triangle instanceof Shape);
assertTrue(isoscelesTriangle instanceof Shape);
assertFalse(nonspecificShape instanceof Shape);
assertTrue(shape instanceof Triangle);
assertTrue(triangle instanceof Triangle);
assertTrue(isoscelesTriangle instanceof Triangle);
assertFalse(nonspecificShape instanceof Triangle);
assertFalse(shape instanceof IsoscelesTriangle);
assertFalse(triangle instanceof IsoscelesTriangle);
assertTrue(isoscelesTriangle instanceof IsoscelesTriangle);
assertFalse(nonspecificShape instanceof IsoscelesTriangle);
上記のコードスニペットを使用すると、右側のタイプが左側のオブジェクトよりも一般的であることがわかります。 具体的には、instanceof演算子はnull値をfalseに処理します。
4. Class.isInstance
ClassクラスのisInstanceメソッドは、instanceof演算子と同等です。 isInstance メソッドは、動的に使用できるため、Java1.1で導入されました。 通常、このメソッドは、引数が null でない場合、 true を返し、ClassCastExceptionを発生させることなく参照型に正常にキャストできます。
定義したインターフェイスとクラスでisInstanceメソッドを使用する方法を見てみましょう。
Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Triangle isoscelesTriangle2 = new IsoscelesTriangle();
Shape nonspecificShape = null;
assertTrue(Shape.class.isInstance(shape));
assertTrue(Shape.class.isInstance(triangle));
assertTrue(Shape.class.isInstance(isoscelesTriangle));
assertTrue(Shape.class.isInstance(isoscelesTriangle2));
assertFalse(Shape.class.isInstance(nonspecificShape));
assertTrue(Triangle.class.isInstance(shape));
assertTrue(Triangle.class.isInstance(triangle));
assertTrue(Triangle.class.isInstance(isoscelesTriangle));
assertTrue(Triangle.class.isInstance(isoscelesTriangle2));
assertFalse(IsoscelesTriangle.class.isInstance(shape));
assertFalse(IsoscelesTriangle.class.isInstance(triangle));
assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle));
assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle2));
ご覧のとおり、右側は、左側と同等か、より具体的です。 特に、nullをisInstanceメソッドに指定すると、falseが返されます。
5. Class.isAssignableFrom
Class.isAssignableFrom メソッドは、ステートメントの左側にある Class が、 Classパラメーターを提供しました。
isAssignableFromメソッドでクラスを使用してみましょう。
Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Triangle isoscelesTriangle2 = new IsoscelesTriangle();
assertFalse(shape.getClass().isAssignableFrom(Shape.class));
assertTrue(shape.getClass().isAssignableFrom(shape.getClass()));
assertTrue(shape.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
assertFalse(triangle.getClass().isAssignableFrom(Shape.class));
assertTrue(triangle.getClass().isAssignableFrom(shape.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(Shape.class));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(shape.getClass()));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(Shape.class));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(shape.getClass()));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));
isInstance の例と同様に、右側は左側と同じか、より具体的でなければならないことがはっきりとわかります。 また、Shapeインターフェイスを割り当てることができないこともわかります。
6. 違い
いくつかの詳細な例を示したので、いくつかの違いを見ていきましょう。
6.1. 意味の違い
表面的には、instanceofはJava言語のキーワードです。 対照的に、isInstanceとisAssignableFromはどちらも、Classタイプのネイティブメソッドです。
意味的には、これらを使用して、2つのプログラミング要素間の異なる関係を検証します。
- 2つのオブジェクト:2つのオブジェクトが同一か等しいかどうかをテストできます。
- オブジェクトと型:オブジェクトが型のインスタンスであるかどうかを確認できます。 明らかに、instanceofキーワードとisInstanceメソッドの両方がこのカテゴリに属します。
- 2つのタイプ: isAssignableFrom メソッドなど、1つのタイプが別のタイプと互換性があるかどうかを調べることができます。
6.2. 使用法のコーナーケースの違い
まず、nullの値が異なります。
assertFalse(null instanceof Shape);
assertFalse(Shape.class.isInstance(null));
assertFalse(Shape.class.isAssignableFrom(null)); // NullPointerException
上記のコードスニペットから、instanceofとisInstanceの両方がfalseを返します。 ただし、isAssignableFromメソッドはNullPointerExceptionをスローします。
第二に、それらはプリミティブタイプによって異なります。
assertFalse(10 instanceof int); // illegal
assertFalse(int.class.isInstance(10));
assertTrue(Integer.class.isInstance(10));
assertTrue(int.class.isAssignableFrom(int.class));
assertFalse(float.class.isAssignableFrom(int.class));
ご覧のとおり、instanceofキーワードはプリミティブ型をサポートしていません。 isInstanceメソッドをint値で使用すると、Javaコンパイラはint値をIntegerオブジェクトに変換します。 したがって、 isInstance メソッドはプリミティブ型をサポートしますが、常にfalseを返します。 isAssignableFrom メソッドを使用すると、結果は正確な型の値によって異なります。
第三に、それらはクラスインスタンス変数とは異なります。
Shape shape = new Triangle();
Triangle triangle = new Triangle();
Class<?> clazz = shape.getClass();
assertFalse(triangle instanceof clazz); // illegal
assertTrue(clazz.isInstance(triangle));
assertTrue(clazz.isAssignableFrom(triangle.getClass()));
上記のコードスニペットから、isInstanceメソッドとisAssignableFromメソッドの両方がクラスインスタンス変数をサポートしているが、instanceofキーワードはサポートしていないことがわかります。
6.3. バイトコードの違い
コンパイルされたクラスファイルでは、それらは異なるオペコードを利用します。
- instanceofキーワードはinstanceofオペコードに対応します
- isInstanceメソッドとisAssignableFromメソッドはどちらも、invokevirtualオペコードを使用します
JVM命令セットでは、 instanceof オペコードの値は193であり、2バイトのオペランドがあります。
instanceof
indexbyte1
indexbyte2
次に、JVMは計算します (indexbyte1 << 8)| indexbyte2 に索引 。 そして、この index は、現在のクラスの時定数プールを指します。 インデックスでは、実行時定数プールにCONSTANT_Class_info定数へのシンボリック参照が含まれています。 また、この定数は、instanceofキーワードの右側に必要な値です。
さらに、instanceofキーワードがクラスインスタンス変数を使用できない理由についても説明します。 これは、 instanceof opcodeが実行時定数プールに定数型を必要とし、定数型をクラスインスタンス変数に置き換えることができないためです。
また、 instanceof キーワードの左側のオブジェクト情報はどこに保存されていますか? オペランドスタックの一番上にあります。 したがって、 instanceof キーワードには、オペランドスタック上のオブジェクトと、実行時定数プール内の定数型が必要です。
JVM命令セットでは、 invokevirtual オペコードの値は182であり、2バイトのオペランドもあります。
invokevirtual
indexbyte1
indexbyte2
同様に、JVMは計算します (indexbyte1 << 8)| indexbyte2 に索引 。 index で、実行時定数プールはCONSTANT_Methodref_info定数へのシンボリック参照を保持します。 この定数には、クラス名、メソッド名、メソッド記述子などのターゲットメソッド情報が含まれます。
isInstance メソッドには、オペランドスタックに2つの要素が必要です。最初の要素は型です。 2番目の要素はオブジェクトです。 ただし、 isAssignableFrom メソッドには、オペランドスタックに2つの型要素が必要です。
6.4. まとめ
要約すると、表を使用してそれらの違いを説明しましょう。
財産 | instanceof | Class.isInstance | Class.isAssignableFrom |
---|---|---|---|
親切 | キーワード | ネイティブメソッド | ネイティブメソッド |
オペランド | オブジェクトとタイプ | タイプとオブジェクト | あるタイプと別のタイプ |
ヌル処理 | 間違い | 間違い | NullPointerException |
プリミティブタイプ | サポートされていません | サポートされていますが、常に false | はい |
クラスインスタンス変数 | いいえ | はい | はい |
バイトコード | instanceof | invokevirtual | invokevirtual |
最適な場合 | オブジェクトが指定され、コンパイル時にタイプが認識されます | オブジェクトが指定されていますが、コンパイルタイプでターゲットタイプが不明です | オブジェクトは指定されておらず、タイプのみが既知であり、実行時のみです |
ユースケース | 毎日の使用、ほとんどの場合に適しています | Reflection APIを使用したライブラリやユーティリティの実装など、複雑で非典型的なケース |
7. 結論
このチュートリアルでは、 instanceof 、 Class.isInstance 、および Class.isAssignableFrom メソッドを確認し、それらの使用法と違いを調べました。
いつものように、このチュートリアルのサンプルコードは、GitHubのにあります。