Java の Class クラスに定義されているメソッドを見ていくシリーズ(目次)。 今回はクラス階層とインスタンスに関連するメソッドを見ていきます。
クラス階層 Class Hierarchy
まずはクラス階層に関連するメソッド。 これらは返り値もしくは引数に Class オブジェクトが割り当てられているもの(の一部)です。 具体的に列挙すると下表のようになります(Class<T> に対するメソッド):メソッド名 | 返り値 | 説明 |
---|---|---|
getSuperclass() getGenericSuperclass() |
Class<? super T> Type |
スーパークラスを返す |
getInterfaces() getGenericInterfaces() |
Class<?> Type |
実装インターフェース群を配列として返す |
asSubclass(Class<U> clazz) | <U> Class<? extends U> | 引数のクラスがスーパークラスならこのクラス自身を返す |
isAssignableFrom(Class<?> cls) | boolean | 引数のクラスがサブクラスなら true を返す |
Type インターフェースの完全修飾名は java.lang.reflect.Type です(Java 5 にて導入)。 これは「型パラメータの情報を含んだ Class クラス」みたいなものだと思います。 以下で、各メソッドをもう少し具体的に見ていきましょう。 サンプルに用いたクラスのクラス図はこんな感じ:
スーパークラスを返すメソッド
まずはスーパークラスを返すメソッド。- getSuperclass()
- getGenericSuperclass()
getGenericsSuperclass() メソッドは型パラメータの情報も含むので、ジェネリクスを用いたクラスをサンプルに使いましょう。 SuperClass, MyClass クラスを以下のように定義します:
public class SuperClass<N extends Number>{...} public class MyClass<N extends Number> extends SuperClass<N>{...}
また、普通このメソッドをインターフェースなどに対して呼んだりはしないと思いますが、一応それらについての実行結果を載せておきます。
分類 | 例 | getSuperclass() | getGenericSuperclass() |
---|---|---|---|
クラス | MyClass.class | SuperClass.class | SuperClass<N>*1 |
Object クラス | Object.class | null | null |
プリミティブ型 | int.class | null | null |
インターフェース | MyInterface.class | null | null |
アノテーション | MyAnnotation.class | null | null |
配列 | String[].class | Object.class | Object.class |
列挙 | MyEnum.class | Enum.class*2 | Enum<MyEnum> |
プロキシ | MyProxy.getClass() | Proxy.class*3 | Proxy.class |
配列と列挙型はそれぞれ Object クラス、Enum クラスを返すので、それらのインスタンスがオブジェクトとして扱われているのが分かりますね。 また、java.lang.reflect.Proxy クラスによって生成したプロキシクラスは全て Proxy クラスを継承しているようです。
実装インターフェースを返すメソッド
次は実装インターフェースを返すメソッド。- getInterfaces()
- getGenericInterfaces()
こちらもジェネリクスを用いたサンプルを書こうと思ったんですが、あまり違いがないので*4、ここではジェネリクスを用いないサンプルに対する実行結果を載せてあります:
分類 | 例 | getInterfaces() | getGenericInterfaces() |
---|---|---|---|
クラス | MyClass.class | [MyInterface.class] | [MyInterface.class] |
Object クラス | Object.class | [] | [] |
プリミティブ型 | int.class | [] | [] |
インターフェース | MyInterface.class | [MySuperInterface.class] | [MySuperInterface.class] |
アノテーション | MyAnnotation.class | [Annotation.class*5] | [Annotation.class] |
配列 | String[].class | [Cloneable.class, Serializable.class] |
[Cloneable.class, Serializable.class] |
列挙 | MyEnum.class | [] | [] |
プロキシ | MyProxy.getClass() | [MyInterface.class] | [MyInterface] |
クラスは実装しているインターフェース、インターフェースは拡張しているインターフェースをそれぞれ返します。 注意が必要なのは、実装しているインターフェースまたは拡張している親クラスにさかのぼってインターフェースを検索しないことでしょうか。 前回見た「アノテーションの取得メソッド」の getDeclaredAnntations() メソッドみたいな感じです。 その意味では getDeclaredInterfaces() みたいな名前の方が良かったのかも知れませんね。
アノテーションと配列はそれぞれデフォルトで実装しているインターフェースがあるようです。 また、プロキシクラスはインターフェースを元にして作成した場合、そのインターフェースを実装するようです。
クラス階層を判定するメソッド
次はクラス階層を判定するメソッド。- asSubclass()
- isAssginableFrom()
asSubclass() メソッド
まずは asSubclass() メソッド。 asSubclass() メソッドの挙動を大雑把に言うと「引数の(Class オブジェクトが表す)クラスがメソッドを呼ばれている(Class オブジェクトが表す)クラスのスーパークラスなら、メソッドを呼ばれた Class オブジェクトを返す」です。 上記の MyClass.class に対して asSubclass() メソッドを呼んだときの挙動は以下のようになります:引数 | 挙動 |
---|---|
SuperClass.class | MyClass.class を返す |
MyClass.class | MyClass.class を返す |
SubClass.class | ClassCastException を投げる |
継承関係のないクラス | ClassCastException を投げる |
サンプルコードはこんな感じ:
assert MyClass.class.asSubclass(SuperClass.class) == MyClass.class; assert MyClass.class.asSubclass(MyInterface.class) == MyClass.class; assert MyClass.class.asSubclass(MyClass.class) == MyClass.class; try{ MyClass.class.asSubclass(SubClass.class); catch(ClassCastException ex){ assert false; }
例外を投げなかった場合、返り値のオブジェクトは常にメソッドを呼び出された Class オブジェクトになるようです。
isAssignableFrom() メソッド
次は isAssignableFrom() メソッド。 挙動を大雑把に言うと「引数の(Class オブジェクトが表す)クラスがメソッドを呼ばれている(Class オブジェクトが表す)クラスのサブクラスなら true を、そうでないなら false を返す」です。 上記の MyClass.class に対して isAssignable() メソッドを呼んだときの挙動は以下のようになります:引数 | 返り値 |
---|---|
SuperClass.class | false |
MyClass.class | true |
SubClass.class | true |
継承関係のないクラス | false |
「assignable(割当可能、代入可能)」かどうかを判定しているので、同じクラスなら true を返します。 サンプルコードはこんな感じ:
SubClass sub = new SubClass(); MyClass mine = sub; // assignable ! assert MyClass.class.isAssignableFrom(SubClass.class) == true; MyClass ours = mine; // assignable ! assert MyClass.class.isAssignableFrom(MyClass.class) == true; assert MyClass.class.isAssignableFrom(SuperClass.class) == false; assert MyClass.class.isAssignableFrom(MyInterface.class) == false;
インスタンス Instance
ここでは Class オブジェクトが表すクラスのインスタンスに関連するメソッドを見ていきます。 扱うクラスは下表の通り(Class<T> に対するメソッド ):メソッド名 | 返り値 |
---|---|
isInstance(Object obj) | boolean |
cast(Object obj) | T |
newInstance() | T |
isInstance() メソッド
isInstance() メソッドは instanceof 演算子と同じです。 ただし、instanceof 演算子は判定するクラス(型)がコードに埋め込まれるのに対して、isInstance() メソッドは対応する Class オブジェクトを変えれば実行中に判定するクラス(型)を変更することができます。SubClass submine = new SubClass(); // instanceof による判定 assert submine instanceof MyClass; // isInstance() による判定 assert MyClass.class.isInstance(subMine);
cast() メソッド
cast() メソッドは () によるキャスト と同じです。 また、こちらも isInstance() メソッドと同じように、対応する Class オブジェクトを変えれば実行中にキャストするクラス(型)を変更することができるかもしれません*6。SubClass submine = new SubClass(); // () によるキャスト MyClass mine0 = (MyClass)submine; // Class リテラルから直接キャスト MyClass mine1 = MyClass.class.cast(submine); // Class オブジェクトを保持して、オブジェクトからキャスト (1) Class<MyClass> type2 = MyClass.class; MyClass mine2 = type2.cast(submine); // Class オブジェクトを保持して、オブジェクトからキャスト (2) Class<? extends MyClass> type3 = MyClass.class; MyClass mine3 = type3.cast(submine); // キャストを行う行でコンパイルエラー! type4 の型宣言がまずい Class<?> type4 = MyClass.class; MyClass mine4 = type4.cast(submine);
newInstance() メソッド
newInstance() メソッドは引数なしのコンストラクタを用いてインスタンスを生成するメソッドです:try{ MyClass m = MyClass.class.newInstance(); }catch(IllegalAccessException ex){ assert false; }catch(InstantiationException ex){ assert false; }
例外処理がチョット面倒。
Effective Java 第2版 (The Java Series)
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2008/11/27
- メディア: 単行本(ソフトカバー)
- 購入: 77人 クリック: 936回
- この商品を含むブログ (266件) を見る
*1:型パラメータの境界 Number がなくなってますが、これは返り値の Type オブジェクトを文字列として出力した際に表示されていなかっただけで、情報が失われたわけではないと思います。
*3:java.lang.reflect.Proxy.class
*4:型パラメータがあるインターフェースは、Class オブジェクトの代わりに対応する java.lang.reflect.Type オブジェクトが返されるだけ。
*5:java.lang.annotation.Annotation.class
*6:キャスト後のオブジェクトを受け取る変数の型を固定しないといけないので単純にはできなさそう。 ジェネリックなメソッドなどを使えばできそうだけど。