当前位置: 首页 > 科技观察

关于Java泛型你必须知道的

时间:2023-03-22 11:02:37 科技观察

-什么是泛型Java泛型(generics)是JDK5引入的新特性,泛型提供了一种编译时类型安全检测机制,允许程序员在编译时检测到非法类型。简单理解就是:泛型在编译时指定类型,减少对象类型不匹配导致的运行时异常。它的主要目的是提高我们代码的重用率。我们Java标准库中的ArrayList就是一个典型的泛型应用:publicclassArrayListextendsAbstractListimplementsList,RandomAccess,Cloneable,java.io.Serializable{...publicArrayList(Collectionc){elementData=c.toArray();if((size=elementData.length)!=??0){//c.toArray可能(错误地)不返回Object[](see6260652)if(elementData.getClass()!=Object[].class)elementData=Arrays.copyOf(elementData,size,Object[].class);}else{//replacewithemptyarray.this.elementData=EMPTY_ELEMENTDATA;}}publicvoidsort(Comparatorc){finalintexpectedModCount=modCount;Arrays.sort((E[])elementData,0,size,c);if(modCount!=expectedModCount){thrownewConcurrentModificationException();}modCount++;}.....publicEget(intindex){rangeCheck(index);returnelementData(index);}publicbooleanadd(Ee){ensureCapacityInternal(size+1);//IncrementsmodCount!!elementData[size++]=e;returntrue;}}在源码中,ArrayList中的E称为类型参数变量,而整个ArrayList我们称它们为泛型类型。我们可以指定除基本类型之外的任何类型,例如:ArrayList。在源代码的集合中?通配符类型表示类型的上界,表示参数化的类型可能是T或者T的子类。源码中的Comparator表示类型的下界(在JavaCore中称为supertypelimitation),表示参数化的类型type是这个类型的超类型(supertype),直到Object。两个extends和super通配符在定义泛型类型Generic时,也可以使用extends通配符来限制T的类型:publicclassGeneric{...}现在,我们只能定义:Genericp1=null;通用<整数>p2=newGeneric<>(1,2);通用<双精度>p3=null;因为Number、Integer和Double都符合。非数字类型将无法编译:Genericp1=null;//compileerror!Genericp2=null;//compileerror!因为String和Object不符合,因为它们不是Number类型或Number的子类。我们来看一个例子:publicclassTest{staticclassFood{}staticclassFruitextendsFood{}staticclassAppleextendsFruit{}staticclassOrangeextendsFruit{}publicvoidtestExtend(){Listlist=newArrayList();//无法安全地添加任何有意义的元素,错误,extends是上界通配符,只能取值,不能放。//因为Fruit的子类不仅有Apple还有Orange,不确定具体泛型是Apple还是Orange,所以任意类型会插入Error//list.add(newApple());//list.add(newOrange());//可以添加null,因为null可以代表任何类型list.add(null);//可以正常获取,使用javaPolymorphicFoodfoot=list.get(0);Appleapple=(Apple)list.get(0);}publicvoidtestSuper(){Listlist=newArrayList();//super是下界通配符,可以存储元素,但只能存储当前类或子类的实例。在当前示例中,list.add(newFruit());list.add(newApple());//无法判断Fruit的父类是否只有一个Food(Object是父类)//所以放入Food的实例编译失败,可以根据java多态的特点只放自己的实例或者放子类实例//list.add(newFood());//Listlist2=newArrayList();//Fruitfruit=list.get(0);//无法确定testExtend方法中的返回类型}},因为泛型使用了extends,在When在列表中存储元素,我们无法确定List中元素的具体类型,即可能是Apple或Orange。所以调用add方法时,无论传入newApple()还是newOrange(),都会出现编译错误。理解了extends之后,再看super就很容易理解了,就是我们无法确定testSuper方法的参数中的泛型是哪个Fruit的父类,所以调用get方法时只能返回Object类型.结合extends可以看出,在获取泛型元素时,使用extends获取的是泛型中上边界的类型(本例为Fruit),范围更小。总结:使用泛型时,访问元素时使用super。获取元素时,使用扩展。有了上面的结论,我们再来看看Java标准库的Collections类定义的copy()方法。copy()方法的定义完美体现了extends和super的意图:copy()方法内部并没有读取dest,因为无法调用dest.get()获取T的引用;copy()方法不会在内部修改src,因为无法调用src.add(T)。publicclassCollections{//将src的每个元素复制到dest:publicstaticvoidcopy(Listdest,Listsrc){for(inti=0;ijsonToMap=fromJson(json);System.out.println(jsonToMap);Animalanimal1=jsonToMap.get("cat");System.out.println(animal1.getEats());}publicstaticTfromJson(Stringstr){returnnewGson().fromJson(str,newTypeToken(){}.getType());}}会提示如下异常:Exceptioninthread"main"java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMapcannotbecasttocom.uaf.rabbitmq.producer.Animalatcom.uaf.rabbitmq.producer.Test2.main(Test2.java:30)异常的原因主要是这句:newGson().fromJson(str,newTypeToken(){}.getType());真正执行这句话的时候,List中的T并没有传入实际的泛型参数,导致Gson跟随了LinkedTreeMap解析JSON,导致错误;这是编译时泛型类型擦除导致的问题;为了解决这个问题,我们需要修改fromJson的方法.setEats("fish");map.put("cat",animal);Stringjson=newGson().toJson(map);System.out.println(json);MapjsonToMap=fromJson(json,newTypeToken>(){}.getType());System.out.println(jsonToMap);Animalanimal1=jsonToMap.get("cat");System.out.println(animal1.getEats()));}publicstaticTfromJson(Stringstr,Typetype){returnnewGson().fromJson(str,type);}}Gson中提供了TypeToken来解决运行时泛型类型擦除的问题。TypeToken是一个帮助我们抓取Map等通用信息的类。上面创建了一个匿名内部类,这样Java编译器就会将泛型信息编译到这个匿名内部类中,然后在运行时使用反射API通过getType()方法提取出来。