イレイジャではジェネリクスの何が消えるのかにて親クラスを継承する際に投入しているジェネリクス型パラメータはclassの情報として残っていることを述べました。
これを利用すると、
public class A<X> {
public class B<Y> extends A<Y> {
public class C extends B<String> {
といった継承階層があった場合に、C.classからA.classの<X>にどのような具象型が適用されているのか(上記例ではString型)をリフレクションで取得することができます。
- Aの型パラメータのプレースホルダ<X>を取得
- Bでsuper-classの型パラメータに渡されたTypeを取得
- 2のTypeが具象型ではなく型パラメータ<Y>なのでさらにサブクラスを走査
- Cでsuper-classの型パラメータに渡されたTypeを取得
- B<Y>のYにString型が渡されていることが分かるのでA<X>のXがStringであることが分かる
といった感じです。型階層がいくらあるのか分からないので再帰で処理するとよいでしょう。
サンプルプログラム
/**
* 渡された型から継承階層を登って、
* 指定の親の型の指定の名前のジェネリクス型パラメータが
* 継承の過程で何型で具現化されているかを走査して返す。
* @param clazz 走査開始する型
* @param targetClass 走査する対象のジェネリクス型パラメータを持つ型。
* 走査開始型の親である必要がある。
* @param targetTypeName 何型で具現化されたを確認したい型パラメータのプレースホルダ名
* @return 具現化された型
*/
public static <T> Class<T> getGenericType(
Class<?> clazz, Class<?> targetClass,
String targetTypeName) {
if (!targetClass.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(
"型" + clazz.getName() + "は、型"
+ targetClass.getName() + "を継承していません");
}
Stack<Class<?>> stack = new Stack<Class<?>>();
while (!targetClass.equals(clazz.getSuperclass())) {
stack.push(clazz);
clazz = clazz.getSuperclass();
}
return getGenericTypeImpl(clazz, targetTypeName, stack);
}
/**
* 型パラメータの具象型取得の実装。再帰処理される。
* @param clazz 現在の走査対象型
* @param targetTypeName 現在の走査対象のジェネリクス型パラメータ名
* @param stack 現在の走査対象型以下の継承階層が積まれたStack
* @return 該当型パラメータの具現化された型
*/
@SuppressWarnings("unchecked")
private static <T> Class<T> getGenericTypeImpl(Class<?> clazz,
String targetTypeName, Stack<Class<?>> stack) {
TypeVariable<? extends Class<?>>[] superGenTypeAray
= clazz.getSuperclass().getTypeParameters();
// 走査対象の型パラメータの名称(Tなど)から宣言のインデックスを取得
int index = 0;
boolean existFlag = false;
for (TypeVariable<? extends Class<?>> type : superGenTypeAray) {
if (targetTypeName.equals(type.getName())) {
existFlag = true;
break;
}
index++;
}
if (!existFlag) {
throw new IllegalArgumentException(
targetTypeName + "に合致するジェネリクス型パラメータがみつかりません");
}
// 走査対象の型パラメータが何型とされているのかを取得
ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
Type y = type.getActualTypeArguments()[index];
// 具象型で継承されている場合
if (y instanceof Class) {
return (Class<T>)y;
}
// ジェネリックパラメータの場合
if (y instanceof TypeVariable) {
TypeVariable<Class<?>> tv = (TypeVariable<Class<?>>)y;
// 再帰して同名の型パラメータを継承階層を下りながら解決を試みる
Class<?> sub = stack.pop();
return getGenericTypeImpl(sub, tv.getName(), stack);
}
// ジェネリック型パラメータを持つ型の場合
if (y instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) y;
return (Class<T>) pt.getRawType();
}
throw new IllegalArgumentException("予期せぬ型 : "
+ y.toString() + " (" + y.getClass() + ")");
}
投稿日時 : 2008年10月29日 0:50