型パラメータで指定されたクラスを取得する。
クラス定義の段階で型パラメータが決まっている場合(以下のようなクラスが該当)、Classから型パラメータで指定された型を得ることができます。(↓のクラスだと、HogeSet.classからSetの型パラメータであるString.classがとれる。)
// クラス定義の段階でSetのパラメータを決めているSetの実装。 class HogeSet implements Set<String> { ...
手順
- Class#getGenericInterfaces(),Class#getGenericSuperclass()で親クラスのTypeを取得できます。
- 戻り値は親クラスやインターフェイスがパラメータ化されている場合ParameterizedTypeになります。
- そうでない場合は、Classが返されます。
- ParameterizedTypeの場合、ParameterizedType#getActualTypeArguments()で型パラメータが取得できます。
- このとき、クラス定義の段階で型パラメータが決まっている場合、パラメータで指定されたClassが返されます。(クラスがパラメータ化されていればParameterizedTypeになるのかも。未検証)
- そうでない場合は、TypeVariableになります。
この手順に従って、指定オブシェクトから型パラメータを取得する関数を書いてみました。
関数の実装。
引数でオブジェクトとクラスを受け取って、クラスの型パラメータを取り出します。
/** * オブジェクトが実装するクラスorインターフェースに指定された型パラメータを取得する。 * * @param obj オブジェクト * @param type 取得対象のパラメータの所有者であるクラス * @return 型パラメータ */ static Type[] getGenericType( Object obj, Class type ) { Class cl = obj.getClass(); while ( cl != null ) { Type t = cl.getGenericSuperclass(); Type[] res = _getGenericType( type, t ); if ( res != null ) { return res; } for ( Type t2 : cl.getGenericInterfaces() ) { res = _getGenericType( type, t2 ); if ( res != null ) { return res; } } cl = cl.getSuperclass(); } return new Type[0]; } private static Type[] _getGenericType( Class type, Type t ) { if ( t != null && t instanceof ParameterizedType && type.equals( ((ParameterizedType) t).getRawType() ) ) { return type != null ? ((ParameterizedType) t).getActualTypeArguments() : new Type[0]; } return null; }
サンプル
テスト用クラス。
// パラメータ化されたインターフェイス static interface InterfaceA<X, Y>{} static interface InterfaceB<X>{} // 親の親のクラス static class ParentParent<X, Y, Z>{} // 親のクラス static class Parent<X, Y> extends ParentParent<X, Y, Boolean>{} // テスト用クラス static class Child<X> extends Parent<X, String> implements InterfaceA<X, Integer>, InterfaceB<X> {}
サンプル。
Child<Date> child = new Child<Date>(); System.out.println( "\n--- Parent" ); for ( Type t : getGenericType( child, Parent.class) ) { System.out.println( t ); } System.out.println( "\n--- ParentParent" ); for ( Type t : getGenericType( child, ParentParent.class) ) { System.out.println( t ); } System.out.println( "\n--- InterfaceA" ); for ( Type t : getGenericType( child, InterfaceA.class) ) { System.out.println( t ); } System.out.println( "\n--- InterfaceB" ); for ( Type t : getGenericType( child, InterfaceB.class) ) { System.out.println( t ); }
実行結果です。XとかYになっているところはTypeVariableが返されています。実際の型は取得できません。
--- Parent X class java.lang.String --- ParentParent X Y class java.lang.Boolean --- InterfaceA X class java.lang.Integer --- InterfaceB X