reflect.Typeインタフェース学習ノート

5666 ワード

最近Mybaitsのソースコードを見て、パラメータ解析を見たとき、このようなコードがありました.
private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class> declaringClass) {
    Class> rawType = (Class>) parameterizedType.getRawType();
    Type[] typeArgs = parameterizedType.getActualTypeArguments();
    Type[] args = new Type[typeArgs.length];
    for (int i = 0; i < typeArgs.length; i++) {
      if (typeArgs[i] instanceof TypeVariable) {
        args[i] = resolveTypeVar((TypeVariable>) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof ParameterizedType) {
        args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof WildcardType) {
        args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
      } else {
        args[i] = typeArgs[i];
      }
    }
    return new ParameterizedTypeImpl(rawType, null, args);
  }

Java reflectパッケージのTypeシステムについて、このシステムについて学習ノートを作成します.
Typeは1つのインタフェースであるが何の方法もない(java 1.8には1つのメソッドgettypeName()が追加されている).その下には4つのサブインタフェースがある:ParameterizedType,GenericArrayType,Type Variable,WildcardType,それぞれパラメータ化タイプ,汎用配列,タイプ変数,ワイルドカード,もう1つのよく知られているClassクラスがあり、実はこのインタフェースの実装クラスであり、一般と基本タイプを代表するものと理解できる.
一、ParameterizedTypeはパラメータ化されたタイプであり、例えばList、Mapなど、3つの方法があり、それぞれgetActualType Arguments()、getRawType()、getOwnerType():
1、getActualTypeArguments()汎用の実際のタイプを取得すると、Mapなどの複数の汎用が存在する可能性があるため、Type[]配列が返されます.
2、getRawType()は汎用的な宣言タイプまたはインタフェースを取得し、MapにはMap(のClassクラス)であり、HashMapにはHashMap(のClassクラス)である.
3、getownerType()は、ソースコードの注釈から「所有者」、すなわち内部クラスの外層クラス、例えばMapを取得することが分かる.entryのOwnerTypeはMapであり,クラスが最上位クラスであればnullを返す. 
public class TestMain {

  private Map map = new HashMap<>();

  public static void main(String[] args) throws NoSuchFieldException {
    Field field = TestMain.class.getDeclaredField("map");
    Type genericType = field.getGenericType();
    ParameterizedType parameterizedType = (ParameterizedType) genericType;
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();


    Type actualTypeArgument = actualTypeArguments[0]; 
    System.out.println(actualTypeArgument);   //java.lang.String
    System.out.println(actualTypeArguments[1]);   //java.lang.Integer

    Type ownerType = parameterizedType.getOwnerType();
    System.out.println(ownerType);  // null

    Type rawType = parameterizedType.getRawType();
    System.out.println(rawType);   // interface  java.util.Map

    System.out.println(genericType);

  }
}

二、GenericArrayTypeは汎用配列であり、例えばList[]、T[]などである.注意Integer[],int[]などはGenericArrayTypeに属さず、通常タイプ(Class,isArray()メソッドはtrueを返す)に属する.Javaでは汎用配列をインスタンス化することが許されないため(理由は『Effective Java II』第25条参照)、反射など他のコンパイラ検出を迂回する方法でのみ汎用配列インスタンスを作成することができ、『Effective Java II』では集合が配列より優れていることを説明する.汎用配列は集合で置き換えることもできるので,実際に遭遇する汎用配列は少なく,このタイプも少ない.
GenericArrayTypeインタフェースには、1つの方法しかありません.getGenericComponentType()は、汎用配列の要素のTypeタイプ、すなわち、List[]のList(ParameterizedType Impl)、T[]のT(Type VariableImpl)を返します.
public class TestMain {
  private List[] listString;
  private List>[] listArray;

  public static void main(String[] args) throws NoSuchFieldException {
    Field listArray = TestMain.class.getDeclaredField("listString");
    Type genericType = listArray.getGenericType();
    GenericArrayType genericArrayType = (GenericArrayType) genericType;
    Type genericComponentType = genericArrayType.getGenericComponentType();
    System.out.println(genericComponentType);   // java.util.List

    Field field = TestMain.class.getDeclaredField("listArray");
    Type genericType1 = field.getGenericType();
    GenericArrayType genericArrayType1 = (GenericArrayType) genericType1;
    Type genericComponentType1 = genericArrayType1.getGenericComponentType();
    System.out.println(genericComponentType1); // java.util.List>

  }
}

三、TypeVariableとWildcardTypeはいずれも<>の内容を記述するインタフェースであり、TypeVariableはタイプパラメータ(<>にない?、例えば)であり、TypeVariableインタフェースにはgetBounds()、getGenericDeclaration()、getName()、getAnnotatedBounds()(java 1.8新規):
getBounds():このタイプの変数の上限、すなわち汎用のextendの右側の値を取得します.例えばList、Numberはタイプ変数Tの上限である.リスト(明示的に定義されていないextends)を単純に宣言した場合、デフォルトはObject(汎用的にTではなくTと記述されている場合、ワイルドカードでは境界を規定することはできません.つまりsuperキーワードは使用できません).
getGenericDeclaration():このタイプの変数エンティティ、すなわちTastMainのTastMainを宣言します.
getName():ソースコードで定義されたタイプ変数の名前、つまりTastMainのTastMainを取得します.
getAnnotatedBounds()://todo
public class TestMain {

  private T t;

  public static void main(String[] args) throws NoSuchFieldException {
    Field field = TestMain.class.getDeclaredField("t");
    Type genericType = field.getGenericType();
    TypeVariable typeVariable = (TypeVariable) genericType;
    Type[] bounds = typeVariable.getBounds();
    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
    String name = typeVariable.getName();
    AnnotatedType[] annotatedBounds = typeVariable.getAnnotatedBounds();
    System.out.println(bounds);   //[Ljava.lang.reflect.Type;@2aae9190
    System.out.println(genericDeclaration);  // class test.TestMain
    System.out.println(name);  // T
    System.out.println(annotatedBounds);  //[Ljava.lang.reflect.AnnotatedType;@2f333739
  }
}

四、WildcardTypeはワイルドカードパラメータ(<>に?があり、例えばextend Number>)、WildcardTypeインタフェースは、getUpperBounds()、getLowerBounds():それぞれ汎用記述のワイルドカードの上境界と下境界を取得する2つの方法がある(比較的簡単で、例を挙げない).