**Java总结系列:Java泛型一、泛型的概念(为什么需要泛型)?首先,让我们看一下下面的短代码:1publicclassGenericTest{23publicstaticvoidmain(String[]args){4Listlist=newArrayList();5list.add("qqyumidi");6list.add("玉米");7列表.添加(100);89for(inti=0;ilist=newArrayList();12list.add("qqyumidi");13list.add("corn");14//列表。添加(100);//1提示编译错误1516for(inti=0;i只能包含String类型的元素,所以不需要在//2处进行强制类型转换,因为此时集合可以记住元素的类型信息,并且编译器已经可以确认它是String类型。结合上面的泛型定义,我们知道在List中,String是一个类型参数,也就是说对应的List接口必须包含一个类型参数。而get()方法的返回结果直接就是这个参数类型(即对应的传入类型参数)。下面就来看看List接口的具体定义:1publicinterfaceListextendsCollection{23intsize();45布尔值isEmpty();67布尔值包含(对象o);89Iteratoriterator();1011Object[]toArray();1213T[]toArray(T[]a);1415booleanadd(Ee);1617booleanremove(Objecto);1819booleancontainsAll(Collection>c);2021booleanaddAll(Collectionc);2223booleanaddAll(intindex,Collectionc);2425booleanremoveAll(Collection>c);2627booleanretainAll(Collection>c);2829voidclear();3031booleanequals(Objecto);3233inthashCode();3435Eget(intindex);3637Eset(intindex,Eelement);3839voidadd(intindex,Eelement);4041Eremove(intindex);4243intindexOf(Objecto);4445intlastIndexOf(Objecto);4647ListIteratorlistIterator();4849ListIteratorlistIterator(intindex);5051ListsubList(intfromIndex,inttoIndex);52}我们可以看到在List接口中采用泛型定义后,中的E表示Typeparameters可以接收特定的类型参数,在这个接口定义中,凡是E出现的地方,都表示接受相同的外部类型参数。自然地,ArrayList是List接口的一个实现类,它的定义形式是:1publicclassArrayListextendsAbstractList2implementsList,RandomAccess,Cloneable,java.io.Serializable{34publicbooleanadd(Ee){5ensureCapacityInternal(size+1);//递增modCount!!6元素数据[大小++]=e;7返回真值;8}910publicEget(intindex){11rangeCheck(index);12checkForComodification();13returnArrayList.this.elementData(offset+index);14}1516//...省略其他具体定义过程1718}由此,我们从源码的角度理解了为什么在//1处添加一个Integer类型的对象是错误的,而在//2处get()的类型直接是String类型。3、自定义泛型接口、泛型类、泛型方法从上面的内容,大家已经了解了泛型的具体运行过程。我还知道接口、类和方法也可以使用泛型来定义并相应地使用。是的,在具体使用上,可以分为泛型接口、泛型类和泛型方法。自定义泛型接口、泛型类和泛型方法类似于上述Java源码中的List和ArrayList。如下,我们看一个最简单的泛型类和方法定义:1publicclassGenericTest{23publicstaticvoidmain(String[]args){45Boxname=newBox("corn");6System.out.println("姓名:"+姓名.getData());7}89}1011类Box{1213privateT数据;1415publicBox(){1617}1819publicBox(Tdata){20this.data=data;21}2223publicTgetData(){24returndata;25}2627}在泛型接口、泛型类和泛型方法的定义过程中,我们常用的参数如T、E、K、V等经常用来表示泛型形式参数,因为它们接收从外部使用传入的类型参数。那么对于不同的传入类型参数,生成的对应对象实例的类型是否相同呢?1publicclassGenericTest{23publicstaticvoidmain(String[]args){45Boxname=newBox("corn");6盒子<整数>年龄=新盒子<整数>(712);78System.out.println("名称类别:"+name.getClass());//com.qqyumidi.Box9System.out.println("年龄段:"+age.getClass());//com.qqyumidi.Box10System.out.println(name.getClass()==age.getClass());//true1112}1314}由此我们发现,在使用泛型类时,虽然传入了不同的泛型参数,但是并没有真正意义上生成不同的类型。内存中传入不同泛型参数的泛型类只有一个,即它仍然是原来最基本的类型(本例中为ForBox),当然逻辑上我们可以理解为多个不同的泛型类型。究其原因,Java中泛型概念的目的在于它只作用于代码编译阶段。在编译过程中,泛型的结果检查正确后,泛型的相关信息会被抹掉,也就是编译成功的class文件不包含任何泛型信息。通用信息不进入运行时阶段。一句话总结:泛型在逻辑上可以看作是多种不同的类型,但它们实际上是同一个基本类型。四。类型通配符根据上面的结论,我们知道Box和Box其实都是Box类型。现在我们需要继续探讨一个问题,那么从逻辑上讲,类似于Box和Box是否可以看作是具有父子关系的泛型?为了弄清楚这个问题,我们继续看下面的例子:1publicclassGenericTest{23publicstaticvoidmain(String[]args){45Boxname=newBox(99);6盒子<整数>年龄=新盒子<整数>(712);78获取数据(名称);910//GenericTest类型中的方法getData(Box)是11//不适用于参数(Box)12getData(age);//11314}1516publicstaticvoidgetData(Boxdata){17System.out.println("data:"+data.getData());18}1920}我们发现出现了错误信息在代码//1:GenericTest类型中的方法getData(Box)不适用于参数(Box)。显然,通过提示信息,我们知道Box在逻辑上不能算是Box的父类。所以为什么?1publicclassGenericTest{23publicstaticvoidmain(String[]args){45Boxa=newBox(712);6盒子<数字>b=a;//17Boxf=newBox(3.14f);8b.setData(f);//2910}1112publicstaticvoidgetData(Boxdata){13System.out.println("data:"+data.getData());14}1516}1718classBox{1920privateTdata;2122publicBox(){2324}2526publicBox(Tdata){27setData(data);28}2930publicTgetData(){31returndata;32}3334publicvoidsetData(Tdata){35this.data=data;36}3738}在这个例子中,显然//1和//2处肯定会有错误提示。这里我们可以用反证法来说明。假设Box在逻辑上可以看作是Box的父类,那么在//1和//2处不会有错误提示,那么问题就出来了,当通过getData()方法它是什么类型?整数?漂浮?还是号码?并且由于编程过程中的顺序不可控,需要进行类型判断,必要时进行强制类型转换。显然,这与泛型的概念相矛盾,因此,从逻辑上讲,不能将Box视为Box的父类。好了,我们回过头来继续看“TypeWildcard”中的第一个例子,我们就知道了其具体错误信息的深层次原因。那么如何解决呢?总部可以定义一个新的功能。这显然违背了Java中的多态概念。因此,我们需要一个逻辑上可以用来表示Box和Box的父类的引用类型。因此,类型通配符应运而生。类型通配符一般使用?而不是特定类型的参数。请注意,这是类型参数,而不是类型参数!而Box>在逻辑上是Box、Box...和所有Box的父类。因此,我们仍然可以定义泛型方法来满足这样的需求。1publicclassGenericTest{23publicstaticvoidmain(String[]args){45Boxname=newBox("corn");6盒子<整数>年龄=新盒子<整数>(712);7Boxnumber=newBox(314);89getData(name);10getData(age);11getData(number);12}1314publicstaticvoidgetData(Box>data){15System.out.println("data:"+data.getData());16}1718}有时候,我们可能还会听到类型通配符的上限和类型通配符的下限。它是什么样的?在上面的例子中,如果需要定义一个方法,其功能类似于getData(),但是对类型参数有进一步的限制:只能是Number类及其子类。在这种情况下,类型通配符的上限是必需的。1publicclassGenericTest{23publicstaticvoidmain(String[]args){45Boxname=newBox("corn");6盒子<整数>年龄=新盒子<整数>(712);7Boxnumber=newBox(314);89getData(姓名);10getData(年龄);11getData(号码);1213//getUpperNumberData(姓名);//114getUpperNumberData(年龄);//215getUpperNumberData(数字);//316}1718publicstaticvoidgetData(Box>data){19System.out.println("data:"+data.getData());20}2122publicstaticvoidgetUpperNumberData(Boxdata){23System.out.println("data:"+data.getData());24}2526}此时,显然,代码中//1处的调用会出现错误提示,而//2//3处的调用则正常。类型通配符的上限定义为Box。相应的,类型通配符的下限是Box。5.本文中的例子外说主要是为了说明泛型中的一些思想,并不一定具有实际的可用性。另外,说到泛型,我相信用的最多的还是集合。其实在实际的编程过程中,可以使用泛型来简化开发,保证代码质量。而且还需要注意的是,Java中并没有所谓的泛型数组。**