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は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つのタイプの具体的な意味をそれぞれご紹介します.次のようなものがあるとしたら
次に例を挙げて説明します.
ParameterizedType
ParameterizedTypeは汎用タイプ、例えばMapを表し、これがParameterizedTypeタイプである.
ParameterizedTypeは、特定に付随する汎用パラメータ(すなわち<>の内容)を取得することができる.getActualType Argumentsは、特定の汎用パラメータを取得することである.getRawTypeは元のタイプに戻る-汎用パラメータを明記したクラスであり、方法(つまり<>左の部分、たとえばParameterizedTypeタイプMapの場合、getRawTypeはMap.classを返します).getOwnerTypeは、このタイプを明示したクラス(内部クラスでは親を返します)を返します.簡単なテストコードです.
TypeVariable
これは汎用パラメータタイプです.例えばMapでは、T 1は汎用パラメータですが、Integerはすでに具体的なパラメータが入っているので、TypeVariableではありません.TypeVariableは,明示された汎用パラメータである.GenericTestのような2つの汎用パラメータT 1,T 2.汎用変数として理解できますが、私たちの具体的な実行時のタイプではありません.
TypeVariableは、この汎用パラメータを明示したタイプを得ることができる.getGenericDeclarationメソッドを使用します.例えばGenericTestでは、2つのTypeVariableのgetGenericDeclarationがGenericTestを返す.class. また,明示的な上界も得られ,getBoundsは汎用パラメータの上界リストを得ることができる.次はTypeVariableの例です.
WildcardType
WildcardTypeはワイルドカードタイプ、つまり
GenericArrayType
これは配列汎型です.例えば
Class
ClassもTypeインタフェースを実現しています.前のコードでjavaをもらいました.lang.Integerなどは、Classに戻ります.Classは、実装されたインタフェースをgetGenericInterfaces()で取得し、親クラスのタイプをgetGenericSuperclass()で取得することもできます.
まとめ
Typeシステム全体は主に前述の内容であり,Method,Field,Class反射からTypeを取得し,Type自体には多くのサブタイプがあり,具体的な汎用タイプを示す.これは私がテストしたコードのソースです.
これらは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自体には多くのサブタイプがあり,具体的な汎用タイプを示す.これは私がテストしたコードのソースです.