按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
//: IInterface。java
// Static inner classes inside interfaces
interface IInterface {
static class Inner {
int i; j; k;
public Inner() {}
void f() {}
}
} ///:~
188
…………………………………………………………Page 190……………………………………………………………
在本书早些时候,我建议大家在每个类里都设置一个main(),将其作为那个类的测试床使用。这样做的一个
缺点就是额外代码的数量太多。若不愿如此,可考虑用一个 static 内部类容纳自己的测试代码。如下所示:
//: TestBed。java
// Putting test code in a static inner class
class TestBed {
TestBed() {}
void f() { System。out。println(〃f()〃); }
public static class Tester {
public static void main(String'' args) {
TestBed t = new TestBed();
t。f();
}
}
} ///:~
这样便生成一个独立的、名为 TestBedTester 的类(为运行程序,请使用“java TestBedTester ”命
令)。可将这个类用于测试,但不需在自己的最终发行版本中包含它。
7。6。5 引用外部类对象
若想生成外部类对象的句柄,就要用一个点号以及一个this 来命名外部类。举个例子来说,在
Sequence。SSelector 类中,它的所有方法都能产生外部类Sequence 的存储句柄,方法是采用Sequence。this
的形式。结果获得的句柄会自动具备正确的类型(这会在编译期间检查并核实,所以不会出现运行期的开
销)。
有些时候,我们想告诉其他某些对象创建它某个内部类的一个对象。为达到这个目的,必须在 new 表达式中
提供指向其他外部类对象的一个句柄,就象下面这样:
//: Parcel11。java
// Creating inner classes
package c07。parcel11;
public class Parcel11 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
public static void main(String'' args) {
Parcel11 p = new Parcel11();
// Must use instance of outer class
// to create an in stances of the inner class:
Parcel11。Contents c = p。new Contents();
Parcel11。Destination d =
p。new Destination(〃Tanzania〃);
}
189
…………………………………………………………Page 191……………………………………………………………
} ///:~
为直接创建内部类的一个对象,不能象大家或许猜想的那样——采用相同的形式,并引用外部类名
Parcel11 。此时,必须利用外部类的一个对象生成内部类的一个对象:
Parcel11。Contents c = p。new Contents();
因此,除非已拥有外部类的一个对象,否则不可能创建内部类的一个对象。这是由于内部类的对象已同创建
它的外部类的对象“默默”地连接到一起。然而,如果生成一个static 内部类,就不需要指向外部类对象的
一个句柄。
7。6。6 从内部类继承
由于内部类构建器必须同封装类对象的一个句柄联系到一起,所以从一个内部类继承的时候,情况会稍微变
得有些复杂。这儿的问题是封装类的“秘密”句柄必须获得初始化,而且在衍生类中不再有一个默认的对象
可以连接。解决这个问题的办法是采用一种特殊的语法,明确建立这种关联:
//: InheritInner。java
// Inheriting an inner class
class WithInner {
class Inner {}
}
public class InheritInner
extends WithInner。Inner {
//! InheritInner() {} // Won't pile
InheritInner(WithInner wi) {
wi。super();
}
public static void main(String'' args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
} ///:~
从中可以看到,InheritInner 只对内部类进行了扩展,没有扩展外部类。但在需要创建一个构建器的时候,
默认对象已经没有意义,我们不能只是传递封装对象的一个句柄。此外,必须在构建器中采用下述语法:
enclosingClassHandle。super();
它提供了必要的句柄,以便程序正确编译。
7。6。7 内部类可以覆盖吗?
若创建一个内部类,然后从封装类继承,并重新定义内部类,那么会出现什么情况呢?也就是说,我们有可
能覆盖一个内部类吗?这看起来似乎是一个非常有用的概念,但“覆盖”一个内部类——好象它是外部类的
另一个方法——这一概念实际不能做任何事情:
//: BigEgg。java
// An inner class cannot be overriden
// like a method
class Egg {
protected class Yolk {
public Yolk() {
System。out。println(〃Egg。Yolk()〃);
190
…………………………………………………………Page 192……………………………………………………………
}
}
private Yolk y;
public Egg() {
System。out。println(〃New Egg()〃);
y = new Yolk();
}
}
public class BigEgg extends Egg {
public class Yolk {
public Yolk() {
System。out。println(〃BigEgg。Yolk()〃);
}
}
public static void main(String'' args) {
new BigEgg();
}
} ///:~
默认构建器是由编译器自动合成的,而且会调用基础类的默认构建器。大家或许会认为由于准备创建一个
BigEgg,所以会使用Yolk 的“被覆盖”版本。但实际情况并非如此。输出如下:
New Egg()
Egg。Yolk()
这个例子简单地揭示出当我们从外部类继承的时候,没有任何额外的内部类继续下去。然而,仍然有可能
“明确”地从内部类继承:
//: BigEgg2。java
// Proper inheritance of an inner class
class Egg2 {
protected class Yolk {
public Yolk() {
System。out。println(〃Egg2。Yolk()〃);
}
public void f() {
System。out。println(〃Egg2。Yolk。f()〃);
}
}
private Yolk y = new Yolk();
public Egg2() {
System。out。println(〃New Egg2()〃);
}
public void insertYolk(Yolk yy) { y = yy; }
public void g() { y。f(); }
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2。Yolk {
public Yolk() {
System。out。println(〃BigEgg2。Yolk()〃);
}
191
…………………………………………………………Page 193……………………………………………………………
public void f() {
System。out。println(〃BigEgg2。Yolk。f()〃);
}
}
public BigEgg2() { insertYolk(new Yolk()); }
public static void main(String'' args) {
Egg2 e2 = new BigEgg2();
e2。g();
}
} ///:~
现在,BigEgg2。Yolk 明确地扩展了Egg2。Yolk,而且覆盖了它的方法。方法 insertYolk()允许BigEgg2 将它
自己的某个Yolk 对象上溯造型至 Egg2 的y 句柄。所以当g()调用y。f()的时候,就会使用f()被覆盖版本。
输出结果如下:
Egg2。Yolk()
New Egg2()
Egg2。Yolk()
BigEgg2。Yolk()
BigEgg2。Yolk。f()
对Egg2。Yolk()的第二个调用是BigEgg2。Yolk 构建器的基础类构建器调用。调用
g()的时候,可发现使用的是f()的被覆盖版本。
7。6。8 内部类标识符
由于每个类都会生成一个。class 文件,用于容纳与如何创建这个类型的对象有关的所有信息(这种信息产生
了一个名为 Class 对象的元类),所以大家或许会猜到内部类也必须生成相应的。class 文件,用来容纳与它
们的Class 对象有关的信息。这些文件或类的名字遵守一种严格的形式:先是封装类的名字,再跟随一个,
再跟随内部类的名字。例如,由 InheritInner。java创建的。class 文件包括:
InheritInner。class
WithInnerInner。class
WithInner。class
如果内部类是匿名的,那么编译器会简单地生成数字,把它们作为内部类标识符使用。若内部类嵌套于其他
内部类中,则它们的名字简单地追加在一个以及外部类标识符的后面。
这种生成内部名称的方法除了非常简单和直观以外,也非常“健壮”,可适应大多数场合的要求(注释
③)。由于它是Java 的标准命名机制,所以产生的文件会自动具备“与平台无关”的能力(注意Java 编译
器会根据情况改变内部类,使其在不同的平台中能正常工作)。
③:但在另一方面,由于“”也是Unix 外壳的一个元字符,所以有时会在列出。class 文件时遇到麻烦。对
一家以Unix 为基础的公司——Sun——来说,采取这种方案显得有些奇怪。我的猜测是他们根本没有仔细考
虑这方面的问题,而是认为我们会将全部注意力自然地放在源码文件上。
7。6。9 为什么要用内部类:控制框架
到目前为止,大家已接触了对内部类的运作进行描述的大量语法与概念。但这些并不能真正说明内部类存在