リフレクションでJava Genericsで指定された型パラメータを取得する
前置き
List<T>への項目追加時に自動的に型変換する仕組みが作りたかったため、 作る方法がないか調べてみた。 総称型の具象化された型パラメータを扱うためのjava.lang.reflectの機能の説明になる。
JavaのGenericsはObject型をObject型以外の型として 透過的に扱える仕組みであるため、 実行時にオブジェクトから型パラメータを取得できない。(.NETはできる)
しかし、クラスのフィールドやメソッドからは具象化された型パラメータの型情報を取得できる。
総称型にも対応するType型
JDK 1.5以降、Class型に対して総称型を扱うランタイム情報を持つ、Type型が導入されている。 この型を実装するインターフェースは下記となる。
Class --- Classオブジェクトも継承している。
GenericArrayType --- 総称配列型 (T)
ParameterizedType --- 総称型(List<T>等)
WildcardType --- ワイルドカード(?)
Listの型パラメータの取得はParameterizedTypeのgetActualTypeArguments()を使えばできる。
Type型取得方法
java.lang.reflectでは下記の方法でTypeオブジェクトを取得できる。
Field field = ....; Type type = field.getGenericType(); Method method = ...; Type type = method.getGenericReturnType(); Type types = method.getGenericParameterTypes();
型定義と取得されるType型の構造サンプル
下記に各種の型定義に対して、どのようなType型が取得されるかを検証するコードとその実行結果を記載する。
package net.dachicraft.toolkit.reflect; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class GenericTest<T> { public List<String> objs1; public Object objs2; public List<List<String>> objs3; public T objs4; public T objs5; public static void main(String args) throws Exception { Type type; Field field1 = GenericTest.class.getField("objs1"); System.out.println("[objs1]"); type = field1.getGenericType(); System.out.println(type + " : " + type.getClass()); ParameterizedType paramType = (ParameterizedType)field1.getGenericType(); for(Type i : paramType.getActualTypeArguments()) { System.out.println(i + " : " + i.getClass()); } Field field2 = GenericTest.class.getField("objs2"); System.out.println("[objs2]"); type = field2.getGenericType(); System.out.println(type + " : " + type.getClass()); Field field3 = GenericTest.class.getField("objs3"); System.out.println("[objs3]"); type = field3.getGenericType(); System.out.println(type + " : " + type.getClass()); type = ( (ParameterizedType)type).getActualTypeArguments()[0]; System.out.println(type + " : " + type.getClass()); type = ( (ParameterizedType)type).getActualTypeArguments()[0]; System.out.println(type + " : " + type.getClass()); Field field4 = GenericTest.class.getField("objs4"); System.out.println("[objs4]"); type = field4.getGenericType(); System.out.println(type + " : " + type.getClass()); Field field5 = GenericTest.class.getField("objs5"); System.out.println("[objs5]"); type = field5.getGenericType(); System.out.println(type + " : " + type.getClass()); type = ( (GenericArrayType)type).getGenericComponentType(); System.out.println(type + " : " + type.getClass()); } }
実行結果
[objs1] java.util.List<java.lang.String> : class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl class java.lang.String : class java.lang.Class [objs2] class java.lang.Object : class java.lang.Class [objs3] java.util.List<java.util.List<java.lang.String>> : class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl java.util.List<java.lang.String> : class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl class java.lang.String : class java.lang.Class [objs4] T : class sun.reflect.generics.reflectiveObjects.TypeVariableImpl [objs5] T[] : class sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl T : class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
objs3のList<List<String>>の定義ではParameterizedTypeが入れ子になっているのが分かる。