Java泛型type体系整理.docx
- 文档编号:28038667
- 上传时间:2023-07-07
- 格式:DOCX
- 页数:10
- 大小:46.39KB
Java泛型type体系整理.docx
《Java泛型type体系整理.docx》由会员分享,可在线阅读,更多相关《Java泛型type体系整理.docx(10页珍藏版)》请在冰豆网上搜索。
Java泛型type体系整理
Java泛型type体系整理
一直对jdk的ref使用比较模糊,早上花了点时间简单的整理了下,也帮助自己理解一下泛型的一些处理。
java中class,method,field的继承体系
java中所有对象的类型定义类Type
说明:
Type:
TypeisthecommonsuperinterfaceforalltypesintheJavaprogramminglanguage.Theseincluderawtypes,parameterizedtypes,arraytypes,typevariablesandprimitivetypes.
使用
一般我们不直接操作Type类型,所以第一次使用会对这个比较陌生,相对内部的一些概念。
根据Type类型分类,整理了一个type->class的转换过程,同理也包括处理GenericType。
支持多级泛型处理。
Java代码
1privatestaticClassgetClass(Typetype,inti){2if(typeinstanceofParameterizedType){//处理泛型类型3returngetGenericClass((ParameterizedType)type,i);4}elseif(typeinstanceofTypeVariable){5return(Class)getClass(((TypeVariable)type).getBounds()[0],0);//处理泛型擦拭对象6}else{//class本身也是type,强制转型7return(Class)type;8}9}1011privatestaticClassgetGenericClass(ParameterizedTypeparameterizedType,inti){12ObjectgenericClass=parameterizedType.getActualTypeArguments()[i];13if(genericClassinstanceofParameterizedType){//处理多级泛型14return(Class)((ParameterizedType)genericClass).getRawType();15}elseif(genericClassinstanceofGenericArrayType){//处理数组泛型16return(Class)((GenericArrayType)genericClass).getGenericComponentType();17}elseif(genericClassinstanceofTypeVariable){//处理泛型擦拭对象18return(Class)getClass(((TypeVariable)genericClass).getBounds()[0],0);19}else{20return(Class)genericClass;21}22}23
测试代码:
Java代码
1interfaceGeneircInteface{23Tmethod1(Tobj);4}56interfaceCommonInteface{78Integermethod2(Integerobj);9}1011classBaseGeneircIntefaceimplementsGeneircInteface{1213protectedRresult;1415@Override16publicRmethod1(Robj){17returnobj;18}1920}2122classGenericClassextendsBaseGeneircIntefaceimplementsGeneircInteface
,CommonInteface{2324@Override25publicListmethod1(Listobj){26result=obj;27returnresult;28}2930publicIntegermethod2(Integerobj){31returnobj;32}3334public
针对class的泛型接口使用:
Java代码
privatestaticvoidclassGeneric(){
System.out.println("\n---------------------classGeneric---------------------");
GenericClassgc=newGenericClass();
Type[]gis=gc.getClass().getGenericInterfaces();//接口的泛型信息
Typegps=gc.getClass().getGenericSuperclass();//父类的泛型信息
TypeVariable[]gtr=gc.getClass().getTypeParameters();//当前接口的参数信息
System.out.println("==============getGenericInterfaces");
for(Typet:
gis){
System.out.println(t+":
"+getClass(t,0));
}
System.out.println("==============getGenericSuperclass");
System.out.println(getClass(gps,0));
System.out.println("==============getTypeParameters");
for(TypeVariablet:
gtr){
StringBuilderstb=newStringBuilder();
for(Typetp:
t.getBounds()){
stb.append(tp+":
");
}
System.out.println(t+":
"+t.getName()+":
"+stb);
}
}
针对method的泛型接口使用:
Java代码
1privatestaticvoidmethodGeneric()throwsException{2System.out.println("\n---------------------methodGeneric---------------------");3GenericClassgc=newGenericClass();4Methodmethod3=gc.getClass().getDeclaredMethod("method3",newClass[]{Object.class});56Type[]gpt3=method3.getGenericParameterTypes();7Type[]get3=method3.getGenericExceptionTypes();8Typegt3=method3.getGenericReturnType();9System.out.println("==============getGenericParameterTypes");10for(Typet:
gpt3){11System.out.println(t+":
"+getClass(t,0));12}13System.out.println("==============getGenericExceptionTypes");14for(Typet:
get3){15System.out.println(t+":
"+getClass(t,0));16}17System.out.println("==============getType");18System.out.println(gt3+":
"+getClass(gt3,0));19}20
针对field的泛型接口使用:
Java代码
1privatestaticvoidfieldGeneric()throwsException{2System.out.println("\n---------------------fieldGeneric---------------------");3GenericClassgc=newGenericClass();4Fieldfield=gc.getClass().getSuperclass().getDeclaredField("result");56Typegt=field.getGenericType();7Typeft=field.getType();8System.out.println("==============getGenericType");9System.out.println(gt+":
"+getClass(gt,0));10System.out.println("==============getType");11System.out.println(ft+":
"+getClass(ft,0));12}
输出结果:
Java代码
1---------------------classGeneric---------------------2==============getGenericInterfaces3com.agapple.misc.GeneircInteface
interfacejava.util.List4interfacecom.agapple.misc.CommonInteface:
interfacecom.agapple.misc.CommonInteface5==============getGenericSuperclass6interfacejava.util.List7==============getTypeParameters89---------------------fieldGeneric---------------------10==============getGenericType11R:
classjava.lang.Object12==============getType13classjava.lang.Object:
classjava.lang.Object1415---------------------methodGeneric---------------------16==============getGenericParameterTypes17T:
classjava.lang.Object18==============getGenericExceptionTypes19E:
classjava.lang.Throwable20==============getType21T:
classjava.lang.Object22
结果说明:
因为泛型的擦拭,对应的GeneircInteface和BaseGeneircInteface,在源码信息已被擦除对应的类型,进行了upper转型,所以取到的是Object。
可以使用extends
GenericClass在类定义时,声明了继承父接口的泛型为List,所以再通过接口和父类获取泛型信息时,是能正确的获取。
通过javap-v可以获取对应的class信息
Java代码
const#46=AscizLcom/agapple/misc/BaseGeneircInteface;>;Lcom/agapple/misc/GeneircInteface;>;Lcom/agapple/misc/CommonInteface;;
而在GenericClass中定义的方法method3,在class信息是一个被向上转型后擦拭的信息。
所以获取method3的相关泛型信息是没有的。
Java代码
1method3;2const#36=Asciz(Ljava/lang/Object;)Ljava/lang/Object;;3const#37=AscizExceptions;4const#38=class#39;//java/lang/Throwable5const#39=Ascizjava/lang/Throwable;6const#40=Asciz(TT;)TT;^TE;;7const#41=AscizTT;;8
思考问题:
Listlist=newArrayList();是否有获取对应的String泛型信息?
不能,临时变量不能保存泛型信息到具体class对象中,List和List对应的class实体是同一个。
Java代码
1GeneircIntefacegi=newGeneircInteface(){23@Override4publicIntegermethod1(Integerobj){5return1;6}78};9
通过匿名类的方式,是否可以获取Integer的泛型信息?
能,匿名类也会在进行classcompiler保存泛型信息。
假如本文例子中的method3,是放在父类中BaseGeneircInteface中进行申明,GenericClass中指定R为List,是否可以获取到对应的泛型信息?
不能,理由和问题1类似。
备注
具体泛型擦拭和信息保存,引用了撒迦的一段回复,解释的挺详尽了。
RednaxelaFX写道
Java泛型有这么一种规律:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
什么意思呢?
“声明一侧”包括泛型类型(泛型类与泛型接口)声明、带有泛型参数的方法和域的声明。
注意局部变量的声明不算在内,那个属于“使用”一侧。
Java代码
1importjava.util.List;2importjava.util.Map;34publicclassGenericClass{//15privateListlist;//26privateMap
上面代码里,带有注释的行里的泛型信息在运行时都还能获取到,原则是源码里写了什么运行时就能得到什么。
针对1的GenericClass,运行时通过Class.getTypeParameters()方法得到的数组可以获取那个“T”;同理,2的T、3的java.lang.String与T、4的T与U都可以获得。
这是因为从Java5开始class文件的格式有了调整,规定这些泛型信息要写到class文件中。
以上面的map为例,通过javap来看它的元数据可以看到记录了这样的信息:
Javap代码
privatejava.util.Mapmap;
Signature:
Ljava/util/Map;
Signature:
length=0x2
000A
乍一看,privatejava.util.Mapmap;不正好显示了它的泛型类型被擦除了么?
但仔细看会发现有两个Signature,下面的一个有两字节的数据,0x0A。
到常量池找到0x0A对应的项,是:
Javap代码
1const#10=AscizLjava/util/Map;;
也就是内容为“Ljava/util/Map;”的一个字符串。
根据Java5开始的新class文件格式规范,方法与域的描述符增添了对泛型信息的记录,用一对尖括号包围泛型参数,其中普通的引用类型用“La/b/c/D;”的格式记录,未绑定值的泛型变量用“Txxx;”的格式记录,其中xxx就是源码中声明的泛型变量名。
类型声明的泛型信息也以类似下面的方式记了下来:
Javap代码
1publicclassGenericClassextendsjava.lang.Object2Signature:
length=0x2300124//...5const#18=AscizLjava/lang/Object;;
详细信息请参考官方文档:
相比之下,“使用一侧”的泛型信息则完全没有被保留下来,在Java源码编译到class文件后就确实丢失了。
也就是说,在方法体内的泛型局部变量、泛型方法调用之类的泛型信息编译后都消失了。
Java代码
1importjava.util.ArrayList;2importjava.util.List;34publicclassTestClass{5publicstaticvoidmain(String[]args){6Listlist=null;//17list=newArrayList();//28for(inti=0;i<10;i++);9}10}
上面代码中,1留下的痕迹是:
main()方法的StackMapTable属性里可以看到:
Java代码
1StackMapTable:
number_of_entries=22frame_type=253/*append*/3offset_delta=124locals=[classjava/util/List,int]5frame_type=250/*chop*/6offset_delta=11
但这里是没有留下泛型信息的。
这段代码只所以写了个空的for循环就是为了迫使javac生成那个StackMapTable,让1多留个影。
如果main()里用到了list的方法,那么那些方法调用点上也会留下1的痕迹,例如如果调用list.add("");,则会留下“java/util/List.add:
(Ljava/lang/Object;)Z”这种记录。
2留下的是“java/util/ArrayList."":
()V”,同样也丢失了泛型信息。
由上述讨论可知,想对带有未绑定的泛型变量的泛型类型获取其实际类型是不现实的,因为class文件里根本没记录实际类型的信息。
觉得这句话太拗口的话用例子来理解:
要想对java.util.List获取E的实际类型是不现实的,因为List.class文件里只记录了E,却没记录使用List时E的实际类型。
想对局部变量等“使用一侧”的已绑定的泛型类型获取其实际类型也不现实,同样是因为class文件中根本没记录这个信息。
例子直接看上面讲“使用一侧”的就可以了。
知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”之类的问题了orz
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 泛型 type 体系 整理