Java汎用Typeシステム

14195 ワード

最近オープンソースコードを見ると、Java汎用型がたくさん入っていて、反射して汎用情報を取得しています.汎用コードを理解するには簡単ですが、自分で汎用を利用してきれいで巧みなフレームワークを書くには、汎用について十分な理解が必要です.だからこの2、3日はJavaの汎用性に関するものを絶えず見ていません.反射によって汎用情報を取得することを理解するには,Typeシステムが理解しなければならない.Javaは1.5から汎用型を導入し,Type体系も導入した.まずTypeのタイプツリーUML図を示し,全体的な認識を得る.
これらはjavaのreflectパケットの下にあり、図中のs付きインタフェースは配列を返し、図面ツールのため、タグは1つのタイプだけで、[]はありません.Java汎用反射の基礎といえる.このUML図では、これらのクラスとインタフェースがTypeに関連する部分だけを選択しました.
Method,Field,Constructor,ClassとType
Method,Field,ConstructorはいずれもTypeに関連付けられており,ClassはTypeのサブクラスである.実際の汎用型から見ても,方法,ドメイン,構造方法ともに汎用型であることができるからである.
これらに接触したことがない場合、最初はこれらの汎用的な反射に接触すると非常に突兀になる.いくつかの疑問から、あるいは需要から考えると、これらのものの役割をよりよく理解できるかもしれません.例えば、反射(1.1からサポートされています)でClassの中の方法とドメインを得ることができることは知っていますが、どのようにしてテンプレートパラメータを持つClassの中のテンプレートパラメータを得ることができますか.例えば、次のようなクラスです.
class Test {
public  void testMethod(R params){
}
}

どうやって?classはTを獲得しますか?Tの具体的なタイプを取得しますか?ClassはインタフェースGenericDeclarationを実現した.GenericDeclarationのgettypeParameters()メソッドでテンプレートパラメータを得ることができます.
同様にMethodがテンプレートパラメータを含むことができる場所は4つあり,テンプレートパラメータ,戻り値,関数パラメータ,異常である.Methodの4つの方法はそれぞれこの4つの場所に対応しています(1つはGenericDeclarationから継承されています).Field、Constructorも似ています.
Method,Field,Constructor,Classには宣言されたテンプレートパラメータを取得する方法があり,反射によってテンプレートパラメータタイプを取得することができる.
Typeのサブインタフェース
Typeには、ParameterizedType、Type Variable、WildcardType、GenericArrayTypeの4つのサブインタフェースがあります.この4つのタイプの具体的な意味をそれぞれご紹介します.次のようなものがあるとしたら
public class GenericTest {
	private Map map = null;
}

次に例を挙げて説明します.
ParameterizedType
ParameterizedTypeは汎用タイプ、例えばMapを表し、これがParameterizedTypeタイプである.
ParameterizedTypeは、特定に付随する汎用パラメータ(すなわち<>の内容)を取得することができる.getActualType Argumentsは、特定の汎用パラメータを取得することである.getRawTypeは元のタイプに戻る-汎用パラメータを明記したクラスであり、方法(つまり<>左の部分、たとえばParameterizedTypeタイプMapの場合、getRawTypeはMap.classを返します).getOwnerTypeは、このタイプを明示したクラス(内部クラスでは親を返します)を返します.簡単なテストコードです.
    	public static void testParameterizedType() throws NoSuchFieldException, SecurityException{
		
		Type mapGenericType = GenericTest.class.getDeclaredField("map").getGenericType();  //ParameterizedType
		if(mapGenericType instanceof ParameterizedType){
			Type basicType = ((ParameterizedType) mapGenericType).getRawType();
			

			System.out.println("basicType equals Map.class is " + (basicType ==Map.class)); //  True
			
			System.out.println("     :"+basicType);  //Map
			//            ,     
			Type[] types = ((ParameterizedType) mapGenericType).getActualTypeArguments();
			for (int i = 0; i < types.length; i++) {
				System.out.println(" "+(i+1)+"      :"+types[i]);
			}
			//    T1, class java.lang.Integer
			
			System.out.println(((ParameterizedType) mapGenericType).getOwnerType());  //null
		}
	}

TypeVariable
これは汎用パラメータタイプです.例えばMapでは、T 1は汎用パラメータですが、Integerはすでに具体的なパラメータが入っているので、TypeVariableではありません.TypeVariableは,明示された汎用パラメータである.GenericTestのような2つの汎用パラメータT 1,T 2.汎用変数として理解できますが、私たちの具体的な実行時のタイプではありません.
TypeVariableは、この汎用パラメータを明示したタイプを得ることができる.getGenericDeclarationメソッドを使用します.例えばGenericTestでは、2つのTypeVariableのgetGenericDeclarationがGenericTestを返す.class. また,明示的な上界も得られ,getBoundsは汎用パラメータの上界リストを得ることができる.次はTypeVariableの例です.
	public static void testTypeVariable() throws NoSuchFieldException, SecurityException{
		
		Type mapGenericType = GenericTest.class.getDeclaredField("map").getGenericType();  //ParameterizedType
		if(mapGenericType instanceof ParameterizedType){
			//            ,     T1, class java.lang.Integer
			Type[] types = ((ParameterizedType) mapGenericType).getActualTypeArguments();
			for (int i = 0; i < types.length; i++) {
				if( types[i] instanceof TypeVariable){
					// T1 is TypeVariable, and Integer is not.
					System.out.println("the "+(i+1)+"th is TypeVariable");
				}else{
					System.out.println("the "+(i+1)+"th is not TypeVariable");
				}
			}
			
		}
		
		System.out.println("GenericTest TypeVariable");
		
		TypeVariable<Class<GenericTest>>[] typeVariables = GenericTest.class.getTypeParameters();
		
		// console print  T1, T2
		for(TypeVariable tmp : typeVariables){
			System.out.println(""+tmp);
			Type[] bounds = tmp.getBounds(); //return upper bounds
			if(bounds.length > 0){ 
				//T2 has upper bounds which is class java.lang.Number, 
				//T1's upper bounds is Object which is default.
				System.out.println("bounds[0] is: "+bounds[0]);
			}
			
			System.out.println("name is: "+tmp.getName());  //  T1, T2
			System.out.println("GenericDeclaration is equals GenericTest.class: "+ (tmp.getGenericDeclaration()==GenericTest.class)); //GenericTest
		}
	}
	

WildcardType
WildcardTypeはワイルドカードタイプ、つまり? extends Numberです.上下の境界を得ることができます.具体的には
	private Map extends Number, ? super Integer> map1 = new HashMap();
	public static void testWildcardType() throws NoSuchFieldException, SecurityException{
		Type mapGenericType = GenericTest.class.getDeclaredField("map1").getGenericType();
		TypeVariable>[] typeVariables = GenericTest.class.getTypeParameters();
		Type[] types = ((ParameterizedType) mapGenericType).getActualTypeArguments();
		for(Type t : types){
			if(t instanceof WildcardType){
				System.out.println("wildcardType");
				
				if( ((WildcardType) t).getLowerBounds().length > 0 )
				System.out.println((((WildcardType) t).getLowerBounds())[0]); //print java.lang.Integer
				
				if( ((WildcardType) t).getUpperBounds().length > 0 )
					System.out.println((((WildcardType) t).getUpperBounds())[0]); //print java.lang.Number, Object
					
			}
		}
	}

GenericArrayType
これは配列汎型です.例えばT1[] tArrayです.これは変数の宣言です.例えば次のコード
	private T1[] tArray = null;
	public static void testGenericArrayType() throws NoSuchFieldException, SecurityException{
		Type tArrayGenericType = GenericTest.class.getDeclaredField("tArray").getGenericType();
		if(tArrayGenericType instanceof GenericArrayType){
			System.out.println("is GenericArrayType");  //
			Type t1 = ((GenericArrayType) tArrayGenericType).getGenericComponentType();
			System.out.println(t1);   // print T1
			if( t1 instanceof TypeVariable){
				System.out.println("t1 is TypeVariable");
				System.out.println(((TypeVariable) t1).getGenericDeclaration());
			}
		}
	}

Class
ClassもTypeインタフェースを実現しています.前のコードでjavaをもらいました.lang.Integerなどは、Classに戻ります.Classは、実装されたインタフェースをgetGenericInterfaces()で取得し、親クラスのタイプをgetGenericSuperclass()で取得することもできます.
まとめ
Typeシステム全体は主に前述の内容であり,Method,Field,Class反射からTypeを取得し,Type自体には多くのサブタイプがあり,具体的な汎用タイプを示す.これは私がテストしたコードのソースです.