Javaでかゆいところに手を伸ばすためにリフレクションAPIを使っているのだけれど、型情報を取得するのはClassを使用する。プリミティブ型でもClassが使えるのでClassばかりよく使っていたのだけどもということで、リフレクションの場合を見つけたので今までのパターンとあわせて簡単にメモだけ。
リフレクションで使うのはClass,Field,Method など定番のものがあり、それ以外はどうするのかという話。
とりあえずパラメータで渡された場合などの判定に使っている方法、か?
プリミティブ型
int, long, doubleなどClassではない型は、該当するClassがあり、その中で Integer.TYPE などTYPEとして定義されているので、型情報を細かく比較すると、どちらかわかるようになっています。
プリミティブ型かObjectかはclassを取得し、Class#isPrimitive() で判定できます。
配列
配列はObjectとして扱えるのでClassを取得、 Class#isArray() で調べられます。要素の型はClass#getComponentType() などで取得できます。
Object型で受け取ったものが配列だった場合、要素の操作はArrayクラスを使います。Listへの変換はArrays.asList((Object[])src) っぽい書き方で可能ですね。
このあたりまで把握していると、とりあえずオブジェクトマッピングっぽいことはできるようになります。
Generics
List など<>を使った表現は、実行中のオブジェクトはnew ArrayList<>() のように作られてしまうので型情報がありません。こういう定義をしているところにいろいろ詰め込もうとすると、変換方法に困ったりしていたのですが、どうやらFieldにはメンバ変数の型情報として持っているのでそちらから取得していくことができます。
class A<T> {
List<Integer> b = new ArrayList<T>();
}
の bの型Listを取得できるということで、new ArrayList() のTなどを特定することはできません。オブジェクトマッピング的なことにしか役に立たないので普通は知らなくていいことかもしれません。
void example(Class<A> cls) {
String name = "b" // 変数名
// private から取得できる。 super classのものは cls.getSuperclass() などで別途取得しないと取れない
Field field = cls.getDeclaredField(name);
または
// public なものを継承関係も含めて取得できる
Field field = cls.getField(name);
Type type = field.getGenericType();
}
type が今回の新要素 Class<A>
の clsのbの型情報が詰まっているTypeという型で、Classの上位型になっているようで、Genericsなど付加要素がある場合はClass以外の形で返ってくるという情報多めのものになっています。
Genericsを使っている場合は interface ParameterizedType 系の情報が返ってきます。
ParameterizedType#getActualTypeArguments() で<>内の型を配列で返してくれます。これもTypeです。多重になっている場合はここでもParameterizedType、そうでない場合はClassだと思っていいと思います。
ParameterizedType.getRawType() で従来のClass相当のTypeが取得できます。List<Integer>
なら Listのclass情報です。
List<? extends C>
という表記も可能なので、この場合はどうなるか…な