- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
8.3 枚举器(反复器)
在任何集合类中,必须通过某种方法在其中置入对象,再用另一种方法从中取得对象。毕竟,容纳各种各样的对象正是集合的首要任务。在 Vector 中,addElement() 便是我们插入对象采用的方法,而 elementAt() 是提取对象的唯一方法。Vector 非常灵活,我们可在任何时候选择任何东西,并可使用不同的索引选择多个元素。
若从更高的角度看这个问题,就会发现它的一个缺陷:需要事先知道集合的准确类型,否则无法使用。乍看来,这一点似乎没什么关系。但假若最开始决定使用 Vector,后来在程序中又决定(考虑执行效率的原因)改变成一个 List(属于 Java1.2 集合库的一部分),这时又该如何做呢?
可利用“反复器”(Iterator)的概念达到这个目的。它可以是一个对象,作用是遍历一系列对象,并选择那个序列中的每个对象,同时不让客户程序员知道或关注那个序列的基础结构。此外,我们通常认为反复器是一种“轻量级”对象;也就是说,创建它只需付出极少的代价。但也正是由于这个原因,我们常发现反复器存在一些似乎很奇怪的限制。例如,有些反复器只能朝一个方向移动。
Java 的 Enumeration(枚举,注释②)便是具有这些限制的一个反复器的例子。除下面这些外,不可再用它做其他任何事情:
(1) 用一个名为 elements() 的方法要求集合为我们提供一个 Enumeration。我们首次调用它的 nextElement() 时,这个 Enumeration 会返回序列中的第一个元素。
(2) 用 nextElement() 获得下一个对象。
(3) 用 hasMoreElements() 检查序列中是否还有更多的对象。
②:“反复器”这个词在 C++和 OOP 的其他地方是经常出现的,所以很难确定为什么 Java 的开发者采用了这样一个奇怪的名字。Java 1.2 的集合库修正了这个问题以及其他许多问题。
只可用 Enumeration 做这些事情,不能再有更多。它属于反复器一种简单的实现方式,但功能依然十分强大。为体会它的运作过程,让我们复习一下本章早些时候提到的 CatsAndDogs.java 程序。在原始版本中,elementAt() 方法用于选择每一个元素,但在下述修订版中,可看到使用了一个“枚举”:
//: CatsAndDogs2.java // Simple collection with Enumeration import java.util.*; class Cat2 { private int catNumber; Cat2(int i) { catNumber = i; } void print() { System.out.println("Cat number " +catNumber); } } class Dog2 { private int dogNumber; Dog2(int i) { dogNumber = i; } void print() { System.out.println("Dog number " +dogNumber); } } public class CatsAndDogs2 { public static void main(String[] args) { Vector cats = new Vector(); for(int i = 0; i < 7; i++) cats.addElement(new Cat2(i)); // Not a problem to add a dog to cats: cats.addElement(new Dog2(7)); Enumeration e = cats.elements(); while(e.hasMoreElements()) ((Cat2)e.nextElement()).print(); // Dog is detected only at run-time } } ///:~
我们看到唯一的改变就是最后几行。不再是:
for(int i = 0; i < cats.size(); i++)
((Cat)cats.elementAt(i)).print();
而是用一个 Enumeration 遍历整个序列:
while(e.hasMoreElements())
((Cat2)e.nextElement()).print();
使用 Enumeration,我们不必关心集合中的元素数量。所有工作均由 hasMoreElements() 和 nextElement() 自动照管了。
下面再看看另一个例子,让我们创建一个常规用途的打印方法:
//: HamsterMaze.java // Using an Enumeration import java.util.*; class Hamster { private int hamsterNumber; Hamster(int i) { hamsterNumber = i; } public String toString() { return "This is Hamster #" + hamsterNumber; } } class Printer { static void printAll(Enumeration e) { while(e.hasMoreElements()) System.out.println( e.nextElement().toString()); } } public class HamsterMaze { public static void main(String[] args) { Vector v = new Vector(); for(int i = 0; i < 3; i++) v.addElement(new Hamster(i)); Printer.printAll(v.elements()); } } ///:~
仔细研究一下打印方法:
static void printAll(Enumeration e) { while(e.hasMoreElements()) System.out.println( e.nextElement().toString()); }
注意其中没有与序列类型有关的信息。我们拥有的全部东西便是 Enumeration。为了解有关序列的情况,一个 Enumeration 便足够了:可取得下一个对象,亦可知道是否已抵达了末尾。取得一系列对象,然后在其中遍历,从而执行一个特定的操作——这是一个颇有价值的编程概念,本书许多地方都会沿用这一思路。
这个看似特殊的例子甚至可以更为通用,因为它使用了常规的 toString() 方法(之所以称为常规,是由于它属于 Object 类的一部分)。下面是调用打印的另一个方法(尽管在效率上可能会差一些):
System.out.println("" + e.nextElement());
它采用了封装到 Java 内部的“自动转换成字串”技术。一旦编译器碰到一个字串,后面跟随一个“+”,就会希望后面又跟随一个字串,并自动调用 toString()。在 Java 1.1 中,第一个字串是不必要的;所有对象都会转换成字串。亦可对此执行一次造型,获得与调用 toString() 同样的效果:
System.out.println((String)e.nextElement())
但我们想做的事情通常并不仅仅是调用 Object 方法,所以会再度面临类型造型的问题。对于自己感兴趣的类型,必须假定自己已获得了一个 Enumeration,然后将结果对象造型成为那种类型(若操作错误,会得到运行期违例)。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论