是最引人注目的。然而,它还有其他的用途。当你生成一个内部类的对象时,此对象与制
造它的外围类的对象(enclosing object)之间就有了一种联系,所以它能访问其外围对
象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问
权3。下面的例子展示了这点:
//: c08:Sequence.java
// Holds a sequence of Objects.
import com.bruceeckel.simpletest.*;
interface Selector {
boolean end();
Object current();
void next();
}
public class Sequence {
private static Test monitor = new Test();
private Object[] objects;
private int next = 0;
public Sequence(int size) { objects = new Object[size]; }
public void add(Object x) {
if(next < objects.length)
objects[next++] = x;
}
private class SSelector implements Selector {
private int i = 0;
public boolean end() { return i == objects.length; }
public Object current() { return objects[i]; }
public void next() { if(i < objects.length) i++; }
}
public Selector getSelector() { return new SSelector(); }
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for(int i = 0; i < 10; i++)
sequence.add(Integer.toString(i));
Selector selector = sequence.getSelector();
while(!selector.end()) {
System.out.println(selector.current());
selector.next();
}
monitor.expect(new String[] {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
});
}
} ///:~
Sequence 类只是一个固定大小的 Object 数组,以类的形式包装了起来。你可以调用
add(),在序列的尾端增加一个新的 Object(只要还有空间)。要获取 Sequence 中的每
一个对象,可以使用 Selector 接口,方法 end()允许你检查序列是否到末尾了,current()
查看当前对象,next() 移到序列中的下一个对象。因为 Selector 是一个接口,所以别的
类可以按它们自己的方式来实现这个接口,并且许多方法能以此接口为参数,来生成通用
的代码。
这里,SSelector 是提供 Selector 功能的 private 类。可以看到,在 main()中创建了一
个 Sequence,并向其中添加了一些 String 对象。然后通过调用 getSelector()获取一个
Selector,并用它在 Sequence 中移动和选择每一个元素。
最初看到 SSelector,可能会觉得它只不过是另一个内部类罢了。但请仔细观察它。注意
方法 end(),current(),和 next()都用到了 objects,这是一个引用,它并不是 SSelector
的一部分,而是外围类的一个 private 成员。所以内部类可以访问其外围类的方法和属性,
就像自己拥有它们似的。这带来了很大的方便,就如前面的例子所示。
内部类自动拥有对其外围类所有成员的访问权。这是如何做到的呢?当某个外围类的对象
创建了一个内部类对象时,此内部类对象必定会保存一个指向那个外围类对象的引用。然
后,在你访问此外围类的成员时,就是用那个“隐藏的”引用来选择外围类的成员。幸运
的是,编译器会帮你处理所有的细节,但你现在也知道了:内部类的对象只能在与其外围
类的对象相关联的情况下才能被创建。构建内部类对象时,需要一个指向其外围类对象的
引用,如果编译器访问不到这个引用就会报错。不过绝大多数时候这都无需程序员操心。