Java泛型定义与用法实例详解
本文实例讲述了Java泛型定义与用法。分享给大家供大家参考,具体如下:
1.泛型的由来
先看如下代码:
importjava.util.List; importjava.util.ArrayList; publicclassTestGeneric{ @SuppressWarnings({"rawtypes","unchecked"}) publicstaticvoidmain(String[]args){ Listlist=newArrayList(); list.add(1); list.add("1"); list.add(newObject()); System.out.println(list); //取值 Integervar1=(Integer)list.get(0); Stringvar2=(String)list.get(1); Objectvar3=list.get(2); System.out.println(var1+""+var2+""+var3); } }
运行结果:
[1,1,java.lang.Object@1db9742]
11java.lang.Object@1db9742
这段代码很简单,将整形、字符串、对象放进list集合中,然后逐一取出。可以看出,由于List接口在定义时并不知道元素的类型,因此默认为Object,即任意类型元素进入list集合后都会自动装箱。而取值的过程更为复杂,所有取得的值都是装箱后的Object对象,必须得知道每一个元素的初始类型才能拆箱。一般使用集合的时候,集合的元素往往都是具有共同特征的,比如同属于一类的----那么,如果一开始限定了list集合元素的类型,那么就可避免上述不规范操作。代码如下,
importjava.util.List; importjava.util.ArrayList; publicclassTestGeneric{ @SuppressWarnings("unused") publicstaticvoidmain(String[]args){ Listlist=newArrayList (); //list.add(1);//报错 //list.add(newObject());//报错 list.add("1"); //取值 Stringvar1=list.get(0);//无需转换 } }
如此一来,便有了泛型集合的说法。实际上,查阅List接口的Api会发现,List接口正是泛型接口,它可以接受一个类型参数E,若不传递参数,则默认是Object类型。
2.泛型类型的继承关系
有如下功能的代码,实现打印任意集合的元素:
importjava.util.List; importjava.util.ArrayList; importjava.util.Collection; publicclassTestGeneric{ //打印任意集合元素 publicvoidprint(Collection
输出:
TestGeneric.java:11:无法将TestGeneric中的print(java.util.Collection
)应用于(java.util.List )
newTestGeneric().print(list);
^
1错误
很明显,意思就是传递的参数类型不匹配。难道String不是继承自Object的吗?没错,String是继承自Object的,但是List
2.1类型通配符
importjava.util.List; importjava.util.ArrayList; importjava.util.Collection; publicclassTestGeneric{ //打印任意集合元素 publicvoidprint(Collection>c){ System.out.println(c); } publicstaticvoidmain(String[]args){ Listlist=newArrayList (); newTestGeneric().print(list); } }
程序正常执行,这里的?表示一个未知类型,这个未知类型与Object不同,List>代表了所有的List<类型>的父类。
2.2泛型方法
不只有通配符可以解决泛型继承的问题,若将上面的方法定义为泛型方法也具有同样的效果:
importjava.util.List; importjava.util.ArrayList; importjava.util.Collection; publicclassTestGeneric{ //打印任意集合元素 publicvoidprint(Collection c){ System.out.println(c); } publicstaticvoidmain(String[]args){ List list=newArrayList (); newTestGeneric().print(list); } }
泛型方法的定义形式如下,
修饰符
其中
2.3泛型方法和通配符的区别
看如下代码:
importjava.util.List; importjava.util.ArrayList; importjava.util.Collection; publicclassTestGeneric{ //打印任意集合元素 publicvoidprint(Collection c1,Collection c2){ System.out.println(c1); System.out.println(c2); } publicstaticvoidmain(String[]args){ List list1=newArrayList (); List list2=newArrayList (); newTestGeneric().print(list1,list2);//传2个father类型 List list3=newArrayList (); List list4=newArrayList (); newTestGeneric().print(list3,list4);//T为child,E为father List list5=newArrayList (); List list6=newArrayList (); newTestGeneric().print(list5,list6);//T为father,E为child,报错 } } classFather{ } classChildextendsFather{ } classOther{ }
上述泛型方法在定义T,E时已经指明了关系:T是E的子类,所以在传递参数的时候,T要么是E的子类,要么就是E本身,所以在传递关系不小心变为EexendsT时,在第三次调用方法时报错了。而如果把上述代码换成?通配符的话,则不具有如此强的限定关系。
总之,泛型方法和?通配符都可以实现未知类型的继承,但是泛型方法主要强调多个未知类型之间的依赖关系。如果只是单纯用作成为一个通用类型的父类这一功能的话,两者都可以实现,反而?通配符较为简洁明了。
2.4泛型参数上、下限的注意
看如下代码:
importjava.util.List; importjava.util.ArrayList; importjava.util.Collection; publicclassTestGeneric{ //复制集合并返回原始集合的最后一个元素 publicTcopy(Collection des,Collectionsrc){ TlastElement=null; for(Tt:src){ lastElement=t; des.add(t); } returnlastElement; } publicstaticvoidmain(String[]args){ List des=newArrayList (); List src=newArrayList (); src.add(newInteger(1)); IntegerlastElement=newTestGeneric().copy(des,src);// System.out.println(lastElement.getClass()); } }
输出:
TestGeneric.java:18:不兼容的类型
找到:java.lang.Number
需要:java.lang.Integer
IntegerlastElement=newTestGeneric().copy(des,src);//
^
1错误
当调用完copy方法后,系统比对发现T类型为Number,?类型为Integer。所以函数返回的T类型是Number了,所以根本不兼容Integer。要修改上面的代码,有俩个办法,
方法1:
改为
NumberlastElement=newTestGeneric().copy(des,src);
分析代码可以得出,?为T的子类,在方法中T=lastElement这句表现为多态,虽然返回的是T类型,但是多态的表现为?类型,即Interger类型,调用lastElement.getClass()也可发现返回的是java.lang.Integer类型,说明此处编译类型为T类型,实际运行类型为?类型。这就好比如下多态转换,
Fatherf=newChild(); Childc=f;//此处一定报错,类型不兼容
虽然f的多态表现为子类Child,但是上面一句连语法检测都过不了。这也就是为什么上面Integer不能兼容Number的原因了。
方法2:
改为
publicTcopy(Collectiondes,Collection src)
这样一来,?类型变为了父类,T类型变为了子类,于是在方法中返回的T类型对象,即lastElement就不具有多态性了。泛型中的上下限是很有学问的,每次看源码时都会琢磨很久,但还是会在浩瀚的接口+泛型的设计中昏迷,这种设计真的完全是为了突出面向对象的特性,以后慢慢琢磨吧。
从这也再次可以看出?通配符在处理具有依赖关系的泛型方法中,显得过于灵活而会导致一些潜在的隐患。
更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。