genericsの型情報
パラメータ付きでインスタンス生成したオブジェクトに対してパラメータ型に何を渡されたかリフレクションで知ることができるかを調査しました。結果をメモしておきます。まさたかさんの初期ikushipeコードが参考になりました。ありがとうございます。さいしょはParameterizedType, TypeVariable, getTypeParameters() がそれぞれ何をしめすか分かりませんでしたので、まずこれを整理しておきます。
以下は値型だけStringに固定したHashMapクラスの例です。
class MyStringMap<K> extends HashMap<K, String> {} ~~~\ ~~~~~~~~~~~~~~~~~~\ getTypeParameters() getGenericSuperClass() \ \ KはTypeVariable型 この場合、instanceof ParameterizedType となる。
ParameterizedTypeでgetGenericSuperClass()をキャストしてgetActualTypeArguments()を呼び出すと、
TypeVariable: K Class: java.lang.String
の順に返ります。getTypeParameters()でも getActualTypeArguments()でも、格納される型は、TypeVariableか、Classか、ParameterizedTypeのどれかとなります。getActualTypeArguments()で、ParameterizedTypeが返る例としては以下のような場合があります。
class ListMapThreadLocal extends ThreadLocal<Map<String, List>> {} ~~~~~~~~~~~~~~~~~\ ここがParameterizedType型 ParameterizedType: Map<String, List>
では次の例を見てみます。
HashMap<Integer, String> map = new HashMap<Integer, String>(); for (Type type: map.getClass().getTypeParameters()) { System.out.println(type.getClass().getSimpleName() + ": " + type); }
Integer型、String型が判定できることを期待したかったのですが、そうなりません。
Class: IntegerClass: String↓ TypeVariable: K TypeVariable: V
Genericsの型情報は、クラスやインターフェースで extends あるいは implements したものは実存する型として.classに残りますが、普通の使い方ではコンパイル時のチェックを堅くすることをして内部的にキャストしてバイトコードに吐き出すだけですので、実行時には消えてしまうためです。