按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
都可以传递给方法。然而,假若造型不正确——如果我们传递了错误的类型——就会在运行期间得到一个违
例错误。这当然没有在编译期进行检查好,但仍然能防止问题的发生。注意在使用这个方法时毋需进行造
型:
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());
就会在编译期间获得一条出错消息。采用这种方式,尽管从编码的角度看显得更令人沉闷,但可以立即判断
出是否使用了正确的类型。
注意在使用 elementAt()时不必进行造型——它肯定是一个Gopher。
3。 参数化类型
这类问题并不是孤立的——我们许多时候都要在其他类型的基础上创建新类型。此时,在编译期间拥有特定
的类型信息是非常有帮助的。这便是“参数化类型”的概念。在C++中,它由语言通过“模板”获得了直接
支持。至少,Java 保留了关键字 generic,期望有一天能够支持参数化类型。但我们现在无法确定这一天何
时会来临。
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 的开发者采用了这
217
…………………………………………………………Page 219……………………………………………………………
样一个奇怪的名字。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())
218
…………………………………………………………Page 220……………………………………………………………
((Cat2)e。nextElement())。print();
使用Enumeration,我们不必关心集合中的元素数量。所有工作均由 hasMoreElements()和nextElement()自
动照管了。
下面再看看另一个例子,让我们创建一个常规用途的打印方法:
//: HamsterMaze。java
// Using an Enumeration
import java。util。*;
class Hamster {