按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
个表达式的结束。因此,它完全等价于在其他任何地方使用分号。
若想对匿名内部类的一个对象进行某种形式的初始化,此时会出现什么情况呢?由于它是匿名的,没有名字
赋给构建器,所以我们不能拥有一个构建器。然而,我们可在定义自己的字段时进行初始化:
//: Parcel8。java
// An anonymous inner class that performs
// initialization。 A briefer version
// of Parcel5。java。
package c07。innerscopes;
public class Parcel8 {
// Argument must be final to use inside
// anonymous inner class:
public Destination dest(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String'' args) {
Parcel8 p = new Parcel8();
Destination d = p。dest(〃Tanzania〃);
}
} ///:~
若试图定义一个匿名内部类,并想使用在匿名内部类外部定义的一个对象,则编译器要求外部对象为final
属性。这正是我们将dest()的自变量设为final 的原因。如果忘记这样做,就会得到一条编译期出错提示。
只要自己只是想分配一个字段,上述方法就肯定可行。但假如需要采取一些类似于构建器的行动,又应怎样
操作呢?通过Java 1。1 的实例初始化,我们可以有效地为一个匿名内部类创建一个构建器:
//: Parcel9。java
// Using 〃instance initialization〃 to perform
// construction on an anonymous inner class
package c07。innerscopes;
public class Parcel9 {
public Destination
dest(final String dest; final float price) {
return new Destination() {
private int cost;
// Instance initialization for each object:
{
cost = Math。round(price);
if(cost 》 100)
System。out。println(〃Over budget!〃);
}
185
…………………………………………………………Page 187……………………………………………………………
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String'' args) {
Parcel9 p = new Parcel9();
Destination d = p。dest(〃Tanzania〃; 101。395F);
}
} ///:~
在实例初始化模块中,我们可看到代码不能作为类初始化模块(即 if语句)的一部分执行。所以实际上,一
个实例初始化模块就是一个匿名内部类的构建器。当然,它的功能是有限的;我们不能对实例初始化模块进
行过载处理,所以只能拥有这些构建器的其中一个。
7。6。3 链接到外部类
迄今为止,我们见到的内部类好象仅仅是一种名字隐藏以及代码组织方案。尽管这些功能非常有用,但似乎
并不特别引人注目。然而,我们还忽略了另一个重要的事实。创建自己的内部类时,那个类的对象同时拥有
指向封装对象(这些对象封装或生成了内部类)的一个链接。所以它们能访问那个封装对象的成员——毋需
取得任何资格。除此以外,内部类拥有对封装类所有元素的访问权限(注释②)。下面这个例子阐示了这个
问题:
//: Sequence。java
// Holds a sequence of Objects
interface Selector {
boolean end();
Object current();
void next();
}
public class Sequence {
private Object'' o;
private int next = 0;
public Sequence(int size) {
o = new Object'size';
}
public void add(Object x) {
if(next 《 o。length) {
o'next' = x;
next++;
}
}
private class SSelector implements Selector {
int i = 0;
public boolean end() {
return i == o。length;
}
public Object current() {
return o'i';
}
public void next() {
if(i 《 o。length) i++;
186
…………………………………………………………Page 188……………………………………………………………
}
}
public Selector getSelector() {
return new SSelector();
}
public static void main(String'' args) {
Sequence s = new Sequence(10);
for(int i = 0; i 《 10; i++)
s。add(Integer。toString(i));
Selector sl = s。getSelector();
while(!sl。end()) {
System。out。println((String)sl。current());
sl。next();
}
}
} ///:~
②:这与C++ “嵌套类”的设计颇有不同,后者只是一种单纯的名字隐藏机制。在C++中,没有指向一个封装
对象的链接,也不存在默认的访问权限。
其中,Sequence 只是一个大小固定的对象数组,有一个类将其封装在内部。我们调用add(),以便将一个新
对象添加到 Sequence 末尾(如果还有地方的话)。为了取得Sequence 中的每一个对象,要使用一个名为
Selector 的接口,它使我们能够知道自己是否位于最末尾(end()),能观看当前对象(current()
Object),以及能够移至 Sequence 内的下一个对象(next() Object )。由于Selector 是一个接口,所以其
他许多类都能用它们自己的方式实现接口,而且许多方法都能将接口作为一个自变量使用,从而创建一般的
代码。
在这里,SSelector 是一个私有类,它提供了 Selector 功能。在main()中,大家可看到Sequence 的创建过
程,在它后面是一系列字串对象的添加。随后,通过对getSelector()的一个调用生成一个Selector 。并用
它在Sequence 中移动,同时选择每一个项目。
从表面看,SSelector 似乎只是另一个内部类。但不要被表面现象迷惑。请注意观察 end(),current()以及
next(),它们每个方法都引用了o。o 是个不属于 SSelector 一部分的句柄,而是位于封装类里的一个
private 字段。然而,内部类可以从封装类访问方法与字段,就象已经拥有了它们一样。这一特征对我们来
说是非常方便的,就象在上面的例子中看到的那样。
因此,我们现在知道一个内部类可以访问封装类的成员。这是如何实现的呢?内部类必须拥有对封装类的特
定对象的一个引用,而封装类的作用就是创建这个内部类。随后,当我们引用封装类的一个成员时,就利用
那个(隐藏)的引用来选择那个成员。幸运的是,编译器会帮助我们照管所有这些细节。但我们现在也可以
理解内部类的一个对象只能与封装类的一个对象联合创建。在这个创建过程中,要求对封装类对象的句柄进
行初始化。若不能访问那个句柄,编译器就会报错。进行所有这些操作的时候,大多数时候都不要求程序员
的任何介入。
7。6。4 static 内部类
为正确理解 static在应用于内部类时的含义,必须记住内部类的对象默认持有创建它的那个封装类的一个对
象的句柄。然而,假如我们说一个内部类是static 的,这种说法却是不成立的。static 内部类意味着:
(1) 为创建一个 static 内部类的对象,我们不需要一个外部类对象。
(2) 不能从 static 内部类的一个对象中访问一个外部类对象。
但在存在一些限制:由于 static 成员只能位于一个类的外部级别,所以内部类不可拥有static 数据或
static 内部类。
倘若为了创建内部类的对象而不需要创建外部类的一个对象,那么可将所有东西都设为static。为了能正常
工作,同时也必须将内部类设为static。如下所示:
//: Parcel10。java
// Static inner classes
187
…………………………………………………………Page 189……………………………………………………………
package c07。parcel10;
abstract class Contents {
abstract public int value();
}
interface Destination {
String readLabel();
}
public class Parcel10 {
private static class PContents
extends Contents {
private int i = 11;
public int value() { return i; }
}
protected static class PDestination
implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
public static Destination dest(String s) {
return new PDestination(s);
}
public static Contents cont() {
return new PContents();
}
public static void main(String'' args) {
Contents c = cont();
Destination d = dest(〃Tanzania〃);
}
} ///:~
在main()中,我们不需要Parcel10 的对象;相反,我们用常规的语法来选择一个 static 成员,以便调用将
句柄返回Contents 和 Destination 的方法。
通常,我们不在一个接口里设置任何代码,但 static 内部类可以成为接口的一部分。由于类是“静态”的,
所以它不会违反接口的规则——static 内部类只位于接口的命名空间内部:
//: IInterface。java
// Static inner classes inside interfaces
interface IInterface {
s