本文转载自微信公众号《潜行》,作者cscw。转载本文请联系SneakUp公众号。1JAVA的Type类型系统先了解下java的Type类型系统(class类=>type),Type就是所有类型(原生类型-Class,参数化类型-Parameterizedtype,数组类型-GenericArrayType,类型变量-TypeVariable,基础Type-类)通用接口;前两个反射和注解中提到的Class是Type的一个实现类,有四个子接口类ParameterizedType、TypeVariable、GenericArrayType、WildcardType。List代表泛型类型,E是TypeVariable类型,而List是ParameterizedType(参数化类型)。List中的String称为实参类型。WildcardTypeGenericArrayType表示元素类型为ParameterizedType(参数化类型)或TypeVariable(类型变量)的数组类型,如T[]或List[]注解只在JDK1.5出现,为了表示注解类型,添加AnnotatedElement类型,字面意思是带注释的元素。JDK1.8有AnnotatedType将Type与注解元素的概念联系起来。AnnotatedType还有四个子接口,分别对应Type的四个子接口。比如ParameterizedType类型如果被注解,就会被编译器解析成AnnotatedParameterizedType:@AnTest("list")Listlist2泛型的概念Java泛型(generics)是JDK1.5引入的一个新特性,它的本质是参数化类型,解决了无法确定具体对象类型的问题;它操作的数据类型被指定为参数(类型参数)。这种参数类型可以用在类、接口、方法和方法的创建中,它们被称为泛型类、泛型接口和泛型方法。类型方法示例泛型类publicclassMainTest{privateTparam;}publicstaticvoidmain(String[]args){MainTestdata=newMainTest(){};ParameterizedTypegenType1=(ParameterizedType)data.getClass()。getGenericSuperclass();}泛型方法的定义而抽象类可以使用泛型4类型擦除创建泛型实例时,jvm会擦除具体类型;编译后的字节码不包含泛型中的类型参数,即ArrayList和ArrayList被擦除成ArrayList,也就是擦除成“原生类型”,这就是泛型擦除publicclassMainTest{publicstaticvoidmain(String[]args){ListstrArr=newArrayList<>();ListintArr=newArrayList<>();TypestrClazz=strArr.getClass();TypeintClazz=intArr.getClass();}}查看编译后的字节码文件是如何表示的:ideamenu->view->showByteCodepublicclassMainTest{Tparam;publicstaticvoidmain(String[]args){MainTesttest=newMainTest<>();test.setParam("siting");}publicTgetParam(){returnparam;}publicvoidsetParam(Tparam){this.param=param;}}publicclasscom/MainTest{...省略publicstaticmain([Ljava/lang/String;)VL0LINENUMBER7L0NEWcom/MainTestDUPINVOKESPECIALcom/MainTest.()VASTORE1L1LINENUMBER8L1ALOAD1LDC"siting"//在类型擦除后调用setParam(Object)INVOKEVIRTUALcom/MainTest.setParam(Ljava/lang/Object;)VL2...省略//getParam的返回值为ObjectpublicgetParam()Ljava/lang/Object;L0LINENUMBER10L0ALOAD0GETFIELDcom/MainTest.param:Ljava/lang/Object;ARETURN...省略//setParam的入参是ObjectpublicsetParam(Ljava/lang/Object;)VL0LINENUMBER11L0ALOAD0ALOAD1PUTFIELDcom/MainTest.param:Ljava/lang/Object;RETURN...}它ca可见T(String)转换为Object类型,缺少初始初始化的String。5泛型继承子类可以指定父类的泛型参数,可以是已知的类(Integer、String等),也可以使用子类本身当泛型参数指定继承泛型类型时,父类泛型参数指定,额外生成的ParameterizedType类型作为子类的父类;如果不指定父类泛型参数,则直接继承原生类型publicclassMainTest{Tparam;staticpublicclassSubTest1extendsMainTest{}staticpublicclassSubTest2extendsMainTest{}//SubTest3继承原始类型staticpublicclassSubTest3extendsMainTest{}}6泛型变量TypeVariable(先临时定义一个名字,Test中的E为泛型参数);泛型变量TypeVariable:泛型泛型参数为TypeVariable;当父类使用子类的泛型参数指定自己的泛型参数时;或者泛型属性定义在泛型类A中,并使用泛型当类A的泛型参数T有类型时,其泛型参数将被编译器定义为泛型变量TypeVariable,而不是被擦除publicclassMainTest{Listparam;publicstaticvoidmain(String[]args)throwsException{Classclazz=MainTest.class;TypeVariable[]typeVariable=clazz.getTypeParameters();//1Fieldfield=clazz.getDeclaredField("param");ParameterizedTypearrayType=(ParameterizedType)字段.getGenericType();//接口的泛型ListTypeE被T实体化了,所以识别为TypeVariableTypeVariablevariable1=(TypeVariable)aarrayType.getActualTypeArguments()[0];//2ParameterizedTypetype=(ParameterizedType)SubTest.class.getGenericSuperclass();TypeVariablevariable2=(TypeVariable)type.getActualTypeArguments()[0];}staticclassSubTestextendsMainTest{}}7参数化类型ParameterizedTypepublicinterfaceParameterizedTypeextendsType{//获取实参,StringinList;如果是List,则为TypeVariabletype[]getActualTypeArguments();//获取原始类型List->ListTypegetRawType();TypegetOwnerType();}注意点,我们不能直接获取指定具体参数的泛型,如Classclazz=List。new创建的对象的类不是ParameterizedType类型,而是泛型本身的类。例子如下编译器可以将参数化泛型识别为ParameterizedType类型。获取ParameterizedType类型的三种方式//1子类继承泛型时,指定具体的参数(可以是String等已知类型,也可以是子类的泛型参数)//2获取定义在泛型内部的属性类,需要指定具体的泛型参数//3本地生成代码,你可以得到ParameterizedType类型publicclassMainTest{Listlist;publicstaticvoidmain(String[]args)throwsNoSuchFieldException{SubTeststr=newSubTest<>();//方法1Classvariable=str.getClass();//父类为(521)ParameterizedTypetypeParameterizedTypegenType=(ParameterizedType)variable.getGenericSuperclass();//(521)ParameterizedType类型的原生类型为(479)classcom.MainTestTypeclazz=genType.getRawType();//MainTest.class的原生类型为(479)classcom.MainTestClassrawClazz=MainTest。class;//方法二、泛型属性Fieldfield=rawClazz.getDeclaredField("list");//属性列表类型为(546)ParameterizedTypetypeListParameterizedTypefieldType=(ParameterizedType)field.getGenericType();//方法三MainTestsub3=newMainTest(){};//clazz3是匿名的SubclassClassclazz3=sub3.getClass();//父类是(555)ParameterizedType类型ParameterizedTypegenType3=(ParameterizedType)clazz3.getGenericSuperclass();//(555)ParameterizedType类型的原生类型是(479)classcom.MainTestTypetype3=genT类型3。getRawType();}publicstaticclassSubTestextendsMainTest{}}8通配符(WildcardType)Unboundedwildcard:Unboundedwildcard?可适配任何引用类型:当方法参数需要传入泛型,且无法确定时直接使用泛型,类型中没有具体的泛型变量,容易造成安全隐患;如果在方法代码中进行了类型转换,很容易引发ClassCastException错误。泛型变量可以用Object代替吗?但是泛型类+具体参数转换后的ParameterizedType(参数化类型)没有继承关系;即Object是String的父类,但是List和List的类型是两个不同的ParameterizedType,没有继承关系。那么有一个类型通配符?publicstaticvoidprint(Listlist){}----->>>publicstaticvoidprint(List>list){}无限通配符可以匹配任何类型;但是在using?的时候,不能给泛型类的变量赋值,因为我们不知道具体的类型是什么;如果强制设置新值,后续读取容易出现ClassCastException错误。因此,编译器将**wildcard?**的泛型限制为可读不可写。上限通配符想接收一个List集合,只能对数值元素进行操作[Float,Integer,Double,Byte可以用List表示List中的元素都是Number的子类publicstaticvoidprint(Listlist){Numbern=newDouble("1.0");list.add(n);Numbertmp=list.get(0);}从图中可以看出,有上界通配符,因为具体type不确定,也是一个只能读不能写的下界通配符classParent{}classChildextendsParent{}publicclassMainTest{Tparam;publicstaticvoidmain(String[]args){MainTestparent_m=newMainTest<>();parent_m.setParam(newChild());Objectparent=parent_m.getParam();}publicTgetParam(){returnparam;}publicvoidsetParam(Tparam){this.param=param;}}如果定义了通配符的父类,则为下界通配符;这种类型的通配符是可读可写的,转换到任何父类都不会出现ClassCastException。个人猜想:是不是因为通配符和上界通配符的泛型向下转换容易出现ClassCastException错误,而下界通配符的向上转换不会导致ClassCastException错误,所以java规范限制了前者编译错误,而后面的编译通过了吗?9通用类型数组(GenericArrayType)publicinterfaceGenericArrayTypeextendsType{//得到这个数组的元素类型,即get:A(A[])orT(T[])TypegetGenericComponentType();}GenericArrayType,泛型数组,描述的是ParameterizedType类型和TypeVariable类型的数组,即:Test[][]、T[]等,是GenericArrayType的子接口publicclassMainTest{T[]param;publicstaticvoidmain(String[]args)throwsException{Classclazz=MainTest.class;Fieldfield=clazz.getDeclaredField("param");GenericArrayTypearrayType=(GenericArrayType)field.getGenericType();TypeVariablevariable=(TypeVariable)arrayType.getGenericComponentType();}}