在任何集合类中必须通过某种方法在其中置入对象再用另一种方法从中取得对象毕竟容纳各种各样的对象正是集合的首要任务在Vector中addElement()便是我们插入对象采用的方法而elementAt()是提取对象的唯一方法Vector非常灵活我们可在任何时候选择任何东西并可使用不同的索引选择多个元素
若从更高的角度看这个问题就会发现它的一个缺陷需要事先知道集合的准确类型否则无法使用乍看来这一点似乎没什么关系但假若最开始决定使用Vector后来在程序中又决定(考虑执行效率的原因)改变成一个List(属于Java集合库的一部分)这时又该如何做呢?
可利用反复器(Iterator)的概念达到这个目的它可以是一个对象作用是遍历一系列对象并选择那个序列中的每个对象同时不让客户程序员知道或关注那个序列的基础结构此外我们通常认为反复器是一种轻量级对象也就是说创建它只需付出极少的代价但也正是由于这个原因我们常发现反复器存在一些似乎很奇怪的限制例如有些反复器只能朝一个方向移动
Java的Enumeration(枚举注释②)便是具有这些限制的一个反复器的例子除下面这些外不可再用它做其他任何事情
() 用一个名为elements()的方法要求集合为我们提供一个Enumeration我们首次调用它的nextElement()时这个Enumeration会返回序列中的第一个元素
() 用nextElement()获得下一个对象
() 用hasMoreElements()检查序列中是否还有更多的对象
②反复器这个词在C++和OOP的其他地方是经常出现的所以很难确定为什么Java的开发者采用了这样一个奇怪的名字Java 的集合库修正了这个问题以及其他许多问题
只可用Enumeration做这些事情不能再有更多它属于反复器一种简单的实现方式但功能依然十分强大为体会它的运作过程让我们复习一下本章早些时候提到的CatsAndDogsjava程序在原始版本中elementAt()方法用于选择每一个元素但在下述修订版中可看到使用了一个枚举
//: CatsAndDogsjava
// Simple collection with Enumeration
import javautil*;
class Cat {
private int catNumber;
Cat(int i) {
catNumber = i;
}
void print() {
Systemoutprintln(Cat number +catNumber);
}
}
class Dog {
private int dogNumber;
Dog(int i) {
dogNumber = i;
}
void print() {
Systemoutprintln(Dog number +dogNumber);
}
}
public class CatsAndDogs {
public static void main(String[] args) {
Vector cats = new Vector();
for(int i = ; 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
}
} ///:~
我们看到唯一的改变就是最后几行。tW.WinGWIt.CoM不再是:
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,然后将结果对象造型成为那种类型(若操作错误,会得到运行期违例)。