Genericsとeraser


この前 Generics を調べてたときに、Genericsなクラスをjadで逆コンパイル掛けるととすっかりGenerics宣言がとっぱらわれてました。
コンパイル時にeraserによって全てのGenerics宣言はきれいさっぱりなくなっちゃうと思ってたけど、そうではないらしい。

イレイジャではジェネリクスの何が消えるのか 凪瀬 Blog
 しかし、型の宣言に関する部分はリフレクションで拾うことができます。ここが混乱する部分ですね。

     * 親クラスを継承する際に投入しているジェネリクス型パラメータ
     * インターフェースを実装する際に投入しているジェネリクス型パラメータ
     * メソッドの引数・戻り値に用いられているジェネリックな型とそこに渡される型パラメータ
     * フィールドに用いられているジェネリックな型とそこに渡される型パラメータ 
へー。
と言うことで検証してみました。
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;

 public class Generics {
     public static void main(String[] args) {
         try {
             Method method = Hoge.class.getMethod("getList", List.class);
             Field field = Hoge.class.getDeclaredField("list");
             System.out.println(method.getGenericParameterTypes()[0]);
             System.out.println(method.getGenericReturnType());
             System.out.println(field.getGenericType());
             System.out.println(Hoge.class.getGenericInterfaces()[0]);
             System.out.println(Hoge.class.getGenericSuperclass());
         } catch (SecurityException e) {
             e.printStackTrace();
         } catch (NoSuchMethodException e) {
             e.printStackTrace();
         } catch (NoSuchFieldException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }

     public static class Hoge extends Fuga<Integer> implements IHoge<String>{
         private List<Number> list;
        
         public List <String> getList(List<Integer> integerList) {
             List<String> stringList = new ArrayList<String>();
             for(Integer intValue : integerList) {
                 stringList.add(intValue.toString());
             }
             return stringList;
         }
     }
     interface IHoge<T> {
     }
     public static class Fuga<T> {
        
     }
 }
実行結果はこんな感じ
 java.util.List<java.lang.Integer>
 java.util.List<java.lang.String>
 Generics.Generics$IHoge<java.lang.String>
 Generics.Generics$Fuga<java.lang.Integer>
 java.util.List<java.lang.Number>
Genericsの情報はgetGenericXXX系のメソッドでとれます。
ソースを追った感じでは、最終的に下記のClassクラスのメソッドで情報をとってるみたいです。
 private native Field[]       getDeclaredFields0(boolean publicOnly);
 private native Method[]      getDeclaredMethods0(boolean publicOnly);
 private native Class[]   getDeclaredClasses0();
ここからはnativeなメソッドなんで追ってませんが、classファイルにはGenericsな情報も一部含まれてるという事みたいですね。
逆コンパイルの結果を信用し過ぎてました。

Comment