友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第76章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






8。2。1  缺点:类型未知  



使用Java 集合的“缺点”是在将对象置入一个集合时丢失了类型信息。之所以会发生这种情况,是由于当初 

编写集合时,那个集合的程序员根本不知道用户到底想把什么类型置入集合。若指示某个集合只允许特定的 

类型,会妨碍它成为一个“常规用途”的工具,为用户带来麻烦。为解决这个问题,集合实际容纳的是类型 

为Object 的一些对象的句柄。这种类型当然代表Java 中的所有对象,因为它是所有类的根。当然,也要注 

意这并不包括基本数据类型,因为它们并不是从“任何东西”继承来的。这是一个很好的方案,只是不适用 



                                                                              213 


…………………………………………………………Page 215……………………………………………………………

下述场合:  

(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()方法,可使我们知道已添加了多少个元素,以便防止误超边界,造 

成违例错误)。  



                                                                                             214 


…………………………………………………………Page 216……………………………………………………………

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));  



                                                                                           215 


…………………………………………………………Page 217……………………………………………………………

    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 {  



                                                                                             216 


…………………………………………………………Page 218……………………………………………………………

  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());  

就会在编译期间获得一条出错消息。采用这种方式,尽管从编码的角度看显得更令人沉闷,但可以立即判断 

出是否使用了正确的类型。  

注意在使用 elemen
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!