日期:2011-03-22 16:17:00 来源:本站整理
java调集的缺陷:典范未知[Java编程]
本文“java调集的缺陷:典范未知[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
利用Java调集的“缺陷”是在将对象置入一个集合时丧失了范例信息.之所以会发生这种情形,是由于当初编写集合时,那个调集的程序员根本不知道用户到底想把什么范例置入调集.若指导某个调集只答应特定的范例,会阻碍它成为一个“通例用处”的工具,为用户带来麻烦.为办理这个问题,调集实际包容的是范例为Object的一些对象的句柄.这种范例当然代表Java中的全部对象,因为它是全部类的根.当然,也要注意这并不包含基本数据范例,因为它们并非从“任何东西”担当来的.这是一个很好的筹划,只是不实用下述场所:
(1) 将一个对象句柄置入集合时,由于范例信息会被丢弃,所以任何范例的对象都可进入我们的调集——即便分外指导它只能包容特定范例的对象.举个例子来说,固然指导它只能包容猫,但事实上任何人都可以把一条狗扔进来.
(2) 由于范例信息不复存在,所以调集能必定的唯一事情就是自己包容的是指向一个对象的句柄.正式利用它之前,必须对其举行造型,使其具有精确的范例.
值得欣喜的是,Java不答应人们滥用置入调集的对象.假定将一条狗扔进一个猫的调集,那么仍会将调集内的全部东西都看做猫,所以在利用那条狗时会得到一个“违例”错误.在一样的意义上,假如试图将一条狗的句柄“造型”到一只猫,那么运行期间仍会得到一个“违例”错误.
下面是个例子:
//: CatsAndDogs.java // Simple collection example (Vector) import java.util.*; class Cat { private int catNumber; Cat(int i) { catNumber = i; } void print() { System.out.println("Cat #" + catNumber); } } class Dog { private int dogNumber; Dog(int i) { dogNumber = i; } void print() { System.out.println("Dog #" + dogNumber); } } public class CatsAndDogs { public static void main(String[] args) { Vector cats = new Vector(); for(int i = 0; i < 7; i++) cats.addElement(new Cat(i)); // Not a problem to add a dog to cats: cats.addElement(new Dog(7)); for(int i = 0; i < cats.size(); i++) ((Cat)cats.elementAt(i)).print(); // Dog is detected only at run-time } } ///:~
可以看出,Vector的利用是非常简单的:先成立一个,再用addElement()置入对象,今后用elementAt()获得那些对象(注意Vector有一个size()办法,可以使我们知道已增添了多少个元素,以便避免误超边界,造成违例错误).
Cat和Dog类都非常浅显——除了都是“对象”之外,它们并没有分外之处(假使不明确指出从什么类担当,就默许为从Object担当.所以我们不但能用Vector办法将Cat对象置入这个调集,也能增添Dog对象,同时不会在编译期和运行期得到任何出错提醒.用Vector办法elementAt()获得本来认为是Cat的对象时,实际得到的是指向一个Object的句柄,必须将那个对象造型为Cat.随后,需求将整个表达式用括号封闭起来,在为Cat调用print()办法之前举行强迫造型;不然就会呈现一个语法错误.在运行期间,假如试图将Dog对象造型为Cat,就会得到一个违例.
这些处理的意义都非常深远.固然显得有些麻烦,但却得到了安全上的保证.我们此后再难无意造成一些躲藏得深的错误.若程序的一个部份(或几个部份)将对象插入一个调集,但我们只是通过一次违例在程序的某个部份发现一个错误的对象置入了调集,就必须找出插入错误的位置.当然,可通过查抄代码到达这个目的,但这大概是最笨的调试工具.另一方面,我们可从一些尺度化的调集类开始自己的编程.固然它们在功效上存在一些不足,且显得有些拙笨,但却能保证没有躲藏的错误.
1. 错误有时并不显暴露来
在某些情形下,程序仿佛精确地工作,不造型回我们本来的范例.第一种情形是相当特别的:String类从编译器得到了额外的帮忙,使其可以正常工作.只要编译器等待的是一个String对象,但它没有得到一个,就会自动调用在Object里定义、并且可以由任何Java类覆盖的toString()办法.这个办法能生成满意要求的String对象,然后在我们需求的时刻利用.
因此,为了让自己类的对象能显示出来,要做的全部事情就是覆盖toString()办法,以下例所示:
//: WorksAnyway.java // In special cases, things just seem // to work correctly. import java.util.*; class Mouse { private int mouseNumber; Mouse(int i) { mouseNumber = i; } // Magic method: public String toString() { return "This is Mouse #" + mouseNumber; } void print(String msg) { if(msg != null) System.out.println(msg); System.out.println( "Mouse number " + mouseNumber); } } class MouseTrap { static void caughtYa(Object m) { Mouse mouse = (Mouse)m; // Cast from Object mouse.print("Caught one!"); } } public class WorksAnyway { public static void main(String[] args) { Vector mice = new Vector(); for(int i = 0; i < 3; i++) mice.addElement(new Mouse(i)); for(int i = 0; i < mice.size(); i++) { // No cast necessary, automatic call // to Object.toString(): System.out.println( "Free mouse: " + mice.elementAt(i)); MouseTrap.caughtYa(mice.elementAt(i)); } } } ///:~
可在Mouse里看到对toString()的重定义代码.在main()的第二个for循环中,可发现下述语句:
System.out.println("Free mouse: " +
mice.elementAt(i));
在“+”后,编译器预期看到的是一个String对象.elementAt()生成了一个Object,所认为得到但愿的String,编译器会默许调用toString().但不幸的是,只有针对String才能得到象这样的后果;其他任何范例都不会举行这样的转换.
躲藏造型的第二种办法已在Mousetrap里得到了利用.caughtYa()办法接纳的不是一个Mouse,而是一个Object.随后再将其造型为一个Mouse.当然,这样做是非常鲁莽的,因为通过接纳一个Object,任何东西都可以传送给办法.但是,假如造型不精确——假如我们传送了错误的范例——就会在运行期间得到一个违例错误.这当然没有在编译期举行查抄好,但仍旧能避免问题的发生.注意在利用这个办法时毋需举行造型:
MouseTrap.caughtYa(mice.elementAt(i));
2. 生成能自动辨别范例的Vector
大家大概不想放弃方才那个问题.一个更“结实”的筹划是用Vector成立一个新类,使其只接纳我们指定的范例,也只生成我们但愿的范例.以下所示:
//: GopherVector.java // A type-conscious Vector import java.util.*; class Gopher { private int gopherNumber; Gopher(int i) { gopherNumber = i; } void print(String msg) { if(msg != null) System.out.println(msg); System.out.println( "Gopher number " + gopherNumber); } } class GopherTrap { static void caughtYa(Gopher g) { g.print("Caught one!"); } } class GopherVector { private Vector v = new Vector(); public void addElement(Gopher m) { v.addElement(m); } public Gopher elementAt(int index) { return (Gopher)v.elementAt(index); } public int size() { return v.size(); } public static void main(String[] args) { GopherVector gophers = new GopherVector(); for(int i = 0; i < 3; i++) gophers.addElement(new Gopher(i)); for(int i = 0; i < gophers.size(); i++) GopherTrap.caughtYa(gophers.elementAt(i)); } } ///:~
这前一个例子近似,只是新的GopherVector类有一个范例为Vector的private成员(从Vector担当有些麻烦,来由稍后便知),并且办法也和Vector近似.但是,它不会接纳和产生普通Object,只对Gopher对象感爱好.
由于GopherVector只接纳一个Gopher(地鼠),所以假定我们利用:
gophers.addElement(new Pigeon());
就会在编译期间得到一条出错消息.采取这种方法,固然从编码的角度看显得更令人沉闷,但可以当即判断出能否利用了精确的范例.
注意在利用elementAt()时没必要举行造型——它必定是一个Gopher.
3. 参数化范例
这类问题并非孤独的——我们很多时刻都要在其他范例的底子上成立新范例.此时,在编译期间拥有特定的范例信息是非常有帮忙的.这就是“参数化范例”的概念.在C++中,它由语言通过“模板”得到了直接支持.至少,Java保存了关键字generic,盼望有一天可以支持参数化范例.但我们目前无法肯定这一天什么时刻会到临.
以上是“java调集的缺陷:典范未知[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |
- ·上一篇文章:<b>java的摆列器(反复器)</b>
- ·下一篇文章:调集
- ·中查找“java调集的缺陷:典范未知”更多相关内容
- ·中查找“java调集的缺陷:典范未知”更多相关内容
评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论