扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
小编给大家分享一下Java中泛型机制有什么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
成都地区优秀IDC服务器托管提供商(创新互联公司).为客户提供专业的西部信息中心,四川各地服务器托管,西部信息中心、多线服务器托管.托管咨询专线:18982081108Java 在 5.0 时增加了泛型机制,据说专家们为此花费了 5 年左右的时间(听起来很不容易)。有了泛型之后,尤其是对集合类的使用,就变得更规范了。
看下面这段简单的代码。
ArrayListlist=newArrayList ();list.add("沉默王二");Stringstr=list.get(0);但在没有泛型之前该怎么办呢?
首先,我们需要使用 Object 数组来设计 Arraylist 类。
classArraylist{privateObject[]objs;privateinti=0;publicvoidadd(Objectobj){objs[i++]=obj;}publicObjectget(inti){returnobjs[i];}}然后,我们向 Arraylist 中存取数据。
Arraylistlist=newArraylist();list.add("沉默王二");list.add(newDate());Stringstr=(String)list.get(0);你有没有发现两个问题:
Arraylist 可以存放任何类型的数据(既可以存字符串,也可以混入日期),因为所有类都继承自 Object 类。
从 Arraylist 取出数据的时候需要强制类型转换,因为编译器并不能确定你取的是字符串还是日期。
对比一下,你就能明显地感受到泛型的优秀之处:使用类型参数解决了元素的不确定性——参数类型为 String 的集合中是不允许存放其他类型元素的,取出数据的时候也不需要强制类型转换了。
二哥,怎么设计泛型啊?
三妹啊,你一个小白只要会用泛型就行了,还想设计泛型啊?!不过,既然你想了解,那么哥义不容辞。
首先,我们来按照泛型的标准重新设计一下 Arraylist 类。
classArraylist{privateObject[]elementData;privateintsize=0;publicArraylist(intinitialCapacity){this.elementData=newObject[initialCapacity];}publicbooleanadd(Ee){elementData[size++]=e;returntrue;}EelementData(intindex){return(E)elementData[index];}}
一个泛型类就是具有一个或多个类型变量的类。Arraylist 类引入的类型变量为 E(Element,元素的首字母),使用尖括号 <> 括起来,放在类名的后面。
然后,我们可以用具体的类型(比如字符串)替换类型变量来实例化泛型类。
Arraylistlist=newArraylist ();list.add("沉默王三");Stringstr=list.get(0);
Date 类型也可以的。
Arraylistlist=newArraylist ();list.add(newDate());Datedate=list.get(0);
其次,我们还可以在一个非泛型的类(或者泛型类)中定义泛型方法。
classArraylist{public T[]toArray(T[]a){return(T[])Arrays.copyOf(elementData,size,a.getClass());}}
不过,说实话,泛型方法的定义看起来略显晦涩。来一副图吧(注意:方法返回类型和方法参数类型至少需要一个)。
现在,我们来调用一下泛型方法。
Arraylistlist=newArraylist<>(4);list.add("沉");list.add("默");list.add("王");list.add("二");String[]strs=newString[4];strs=list.toArray(strs);for(Stringstr:strs){System.out.println(str);}
最后,我们再来说说泛型变量的限定符 extends。在解释这个限定符之前,我们假设有三个类,它们之间的定义是这样的。
classWanglaoer{publicStringtoString(){return"王老二";}}classWangerextendsWanglaoer{publicStringtoString(){return"王二";}}classWangxiaoerextendsWanger{publicStringtoString(){return"王小二";}}
我们使用限定符 extends 来重新设计一下 Arraylist 类。
classArraylist{}
当我们向 Arraylist 中添加 Wanglaoer 元素的时候,编译器会提示错误:Arraylist 只允许添加 Wanger 及其子类 Wangxiaoer 对象,不允许添加其父类 Wanglaoer。
Arraylistlist=newArraylist<>(3);list.add(newWanger());list.add(newWanglaoer());//Themethodadd(Wanger)inthetypeArraylist isnotapplicableforthearguments//(Wanglaoer)list.add(newWangxiaoer());
也就是说,限定符 extends 可以缩小泛型的类型范围。
二哥,听说虚拟机没有泛型?
三妹,你功课做得可以啊,连虚拟机都知道了啊。哥可以肯定地回答你,虚拟机是没有泛型的。
啰嗦一句哈。我们编写的 Java 代码(也就是源码,后缀为 .java 的文件)是不能够被操作系统直接识别的,需要先编译,生成 .class 文件(也就是字节码文件)。然后 Java 虚拟机(JVM)会充当一个翻译官的角色,把字节码翻译给操作系统能听得懂的语言,告诉它该干嘛。
怎么确定虚拟机没有泛型呢?我们需要把泛型类的字节码进行反编译——强烈推荐超神反编译工具 Jad !
现在,在命令行中敲以下代码吧(反编译 Arraylist 的字节码文件 Arraylist.class)。
jadArraylist.class
命令执行完后,会生成一个 Arraylist.jad 的文件,用文本编辑工具打开后的结果如下。
//DecompiledbyJadv1.5.8g.Copyright2001PavelKouznetsov.//Jadhomepage:http://www.kpdus.com/jad.html//Decompileroptions:packimports(3)//SourceFileName:Arraylist.javapackagecom.cmower.java_demo.fanxing;importjava.util.Arrays;classArraylist{publicArraylist(intinitialCapacity){size=0;elementData=newObject[initialCapacity];}publicbooleanadd(Objecte){elementData[size++]=e;returntrue;}ObjectelementData(intindex){returnelementData[index];}privateObjectelementData[];privateintsize;}
类型变量
既然如此,那如果泛型类使用了限定符 extends,结果会怎么样呢?我们先来看看 Arraylist2 的源码。
classArraylist2{privateObject[]elementData;privateintsize=0;publicArraylist2(intinitialCapacity){this.elementData=newObject[initialCapacity];}publicbooleanadd(Ee){elementData[size++]=e;returntrue;}EelementData(intindex){return(E)elementData[index];}}字节码文件Arraylist2.class使用Jad反编译后的结果如下。//DecompiledbyJadv1.5.8g.Copyright2001PavelKouznetsov.//Jadhomepage:http://www.kpdus.com/jad.html//Decompileroptions:packimports(3)//SourceFileName:Arraylist2.javapackagecom.cmower.java_demo.fanxing;//Referencedclassesofpackagecom.cmower.java_demo.fanxing://WangerclassArraylist2{publicArraylist2(intinitialCapacity){size=0;elementData=newObject[initialCapacity];}publicbooleanadd(Wangere){elementData[size++]=e;returntrue;}WangerelementData(intindex){return(Wanger)elementData[index];}privateObjectelementData[];privateintsize;}
类型变量
通过以上两个例子说明,Java 虚拟机会将泛型的类型变量擦除,并替换为限定类型(没有限定的话,就用 Object)。
二哥,类型擦除会有什么问题吗?
三妹啊,你还别说,类型擦除真的会有一些“问题”。
我们来看一下这段代码。
publicclassCmower{publicstaticvoidmethod(Arraylistlist){System.out.println("Arraylist list");}publicstaticvoidmethod(Arraylist list){System.out.println("Arraylist list");}}
在浅层的意识上,我们会想当然地认为 Arraylist
但由于类型擦除的原因,以上代码是不会通过编译的——编译器会提示一个错误(这正是类型擦除引发的那些“问题”):
Erasure of method method(Arraylist) is the same as another method in type
CmowerErasure of method method(Arraylist) is the same as another method in type
Cmower
大致的意思就是,这两个方法的参数类型在擦除后是相同的。
也就是说,method(Arraylist
有句俗话叫做:“百闻不如一见”,但即使见到了也未必为真——泛型的擦除问题就可以很好地佐证这个观点。
二哥,听说泛型还有通配符?
三妹啊,哥突然觉得你很适合作一枚可爱的程序媛啊!你这预习的功课做得可真到家啊,连通配符都知道!
通配符使用英文的问号(?)来表示。在我们创建一个泛型对象时,可以使用关键字 extends 限定子类,也可以使用关键字 super 限定父类。
为了更好地解释通配符,我们需要对 Arraylist 进行一些改进。
classArraylist{privateObject[]elementData;privateintsize=0;publicArraylist(intinitialCapacity){this.elementData=newObject[initialCapacity];}publicbooleanadd(Ee){elementData[size++]=e;returntrue;}publicEget(intindex){return(E)elementData[index];}publicintindexOf(Objecto){if(o==null){for(inti=0;i =0;}publicStringtoString(){StringBuildersb=newStringBuilder();for(Objecto:elementData){if(o!=null){Ee=(E)o;sb.append(e.toString());sb.append(',').append('');}}returnsb.toString();}publicintsize(){returnsize;}publicEset(intindex,Eelement){EoldValue=(E)elementData[index];elementData[index]=element;returnoldValue;}}
1)新增 indexOf(Object o) 方法,判断元素在 Arraylist 中的位置。注意参数为 Object 而不是泛型 E。
2)新增 contains(Object o) 方法,判断元素是否在 Arraylist 中。注意参数为 Object 而不是泛型 E。
3)新增 toString() 方法,方便对 Arraylist 进行打印。
4)新增 set(int index, E element) 方法,方便对 Arraylist 元素的更改。
你知道,Arraylist
利用 extends Wanger> 形式的通配符,可以实现泛型的向上转型,来看例子。
Arraylistlist2=newArraylist<>(4);list2.add(null);//list2.add(newWanger());//list2.add(newWangxiaoer());Wangerw2=list2.get(0);//Wangxiaoerw3=list2.get(1);
list2 的类型是 Arraylist extends Wanger>,翻译一下就是,list2 是一个 Arraylist,其类型是 Wanger 及其子类。
注意,“关键”来了!list2 并不允许通过 add(E e) 方法向其添加 Wanger 或者 Wangxiaoer 的对象,唯一例外的是 null。为什么不能存呢?原因还有待探究(苦涩)。
那就奇了怪了,既然不让存放元素,那要 Arraylist extends Wanger> 这样的 list2 有什么用呢?
虽然不能通过 add(E e) 方法往 list2 中添加元素,但可以给它赋值。
Arraylistlist=newArraylist<>(4);Wangerwanger=newWanger();list.add(wanger);Wangxiaoerwangxiaoer=newWangxiaoer();list.add(wangxiaoer);Arraylistlist2=list;Wangerw2=list2.get(1);System.out.println(w2);System.out.println(list2.indexOf(wanger));System.out.println(list2.contains(newWangxiaoer()));
Arraylist extends Wanger> list2 = list; 语句把 list 的值赋予了 list2,此时 list2 == list。由于 list2 不允许往其添加其他元素,所以此时它是安全的——我们可以从容地对 list2 进行 get()、indexOf() 和 contains()。想一想,如果可以向 list2 添加元素的话,这 3 个方法反而变得不太安全,它们的值可能就会变。
利用 super Wanger> 形式的通配符,可以向 Arraylist 中存入父类是 Wanger 的元素,来看例子。
Arraylistlist3=newArraylist<>(4);list3.add(newWanger());list3.add(newWangxiaoer());//Wangerw3=list3.get(0);
需要注意的是,无法从 Arraylist super Wanger> 这样类型的 list3 中取出数据。为什么不能取呢?原因还有待探究(再次苦涩)。
虽然原因有待探究,但结论是明确的: extends T> 可以取数据, super T> 可以存数据。那么利用这一点,我们就可以实现数组的拷贝—— extends T> 作为源(保证源不会发生变化), super T> 作为目标(可以保存值)。
publicclassCollections{publicstaticvoidcopy(Arraylistdest,Arraylistsrc){for(inti=0;i 以上是“Java中泛型机制有什么用”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联-成都网站建设公司行业资讯频道!
新闻标题:Java中泛型机制有什么用-创新互联
分享网址:http://kswjz.com/article/giiio.html
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流