1 嵌套内部类的定义
嵌套内部类就是定义在外围类中的 static 类型内部类。
2 注意事项
嵌套内部类又称嵌套类(少了”内部”两个字)(下面都使用“嵌套类”这个名字),准确的说,它几乎不是个内部类,从这点就可以看出它与其它内部类有很大的不同,分别是以下几点:
- 创建嵌套类时不需要借助外围类的对象,因此也就不需要隐式地指向它的外位列对象,这与之前所有的内部类创建方式都不同。
- 不能从嵌套类的对象中访问非静态的外围类对象,也就是说,此时外围类与嵌套类互不可见。
- 普通内部类的字段与方法只能放在类的外部层次上,所以普通的内部类不能有 static 数据和 static 字段,也不能包含前套类。但是内部类可以包含这些所有的东西。
3 嵌套内部类的简单示例
interface In {
void readLabel();
}
class Outer {
static class Inner implements In { // static 类型内部类
private int label = 810;
public void readLabel() { System.out.println(label); }
}
}
public class Test{
public static void main(String[] args) {
In i = new Outer.Inner(); // 注意这个格式,并没有创建外围类的对象
i.readLabel();
}
}
/* Output
810
*/
解析:上述代码在创建嵌套类对象时并没有依赖外围类的对象。我们通过借用外围类类名来得到嵌套类类名从而直接创建嵌套类对象。
注意:正因为嵌套类与外围类的这个关系,也导致了嵌套类并没有普通内部类特殊的 this 用法——用特殊的 this 引用可以链接到外围类对象从而创建与它隐形连接的外围了对象,这一特性在内部类的创建与使用中有所提及。
4 对嵌套类使用工厂方法
interface In {
void readLabel();
}
class Outer {
private static class Inner implements In { // 私有静态的嵌套类
private int label = 810;
public void readLabel() { System.out.println(label); }
}
public static In getInner() { // 公共静态的工厂方法
return new Inner();
}
}
public class Test{
public static void main(String[] args) {
In i = Outer.getInner(); // 仅仅使用了类型并没有创建外围类的对象
i.readLabel();
}
}
/* Output
810
*/
解析:上述代码中,外围类含有 private static 类型的嵌套类,如果我们想在其他类中创建这个嵌套类的对象就无法做到,如果我们在这个外围类中添加 static(可以访问 static 成员) 类型的工厂方法(方便于返回指定对象),就可以通过这个方法直接得到嵌套类对象,而且因为此方法是 static 类型的,我们不需要外围类对象。
注意:上述解析中用到的主要设计思想是命名空间的使用:我们可以将类定义视为一个空间,在其中 static 成员仅仅是归属于这个空间,并不依赖于类空间的对象。这种思想非常重要,如果你查 Java 的标准文档,你会发现里面含有各种各样的 static 方法,试想一下,如果用任何方法都要创建它所属的类对象,那是多么的麻烦。
5 接口中的嵌套类
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分。放到接口的任何类都会自动变成 public 和 static 类型。与对嵌套类使用工厂方法一样,static 类置于接口中时只是将这个类的定义置于接口的命名空间里,这并不违反接口的规则。我们甚至可以在内部类中实现其外围接口。
示例如下:
interface In {
void readLabel();
class Nesting implements In { // 自动变为 public 与 static 类型
private int label = 810;
public void readLabel() {
System.out.println(label);
}
}
}
public class Test {
public static void main(String[] args) {
In i = new In.Nesting(); // 借用嵌套类所属的接口名来创建嵌套类对象
i.readLabel();
}
}
解析:创建在”接口命名空间内的嵌套类”与”类命名空间的嵌套类”的方式是一样的,都可以通过命名空间名做中转来创建嵌套类。这两者的区别之一是接口中不能含有工厂方法。
技巧:
- 如果想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便。
- 我们通常会为想要被测试的类编写一个 main() 方法以用来测试,但是这有一个缺点:就是必须带着那些已编译过的额外代码。我们可以利用嵌套类来解决这个问题,让 main() 方法置于嵌套类中,这就像一个逻辑上的包装,并没有副作用。
示例如下:
public class Test {
private int i = 810;
public void readLabel() {
System.out.println(i);
}
public static class Tester { // 创建一个包含 main() 方法的嵌套类,便于测试
public static void main(String[] args) {
Test t = new Test();
t.readLabel();
}
}
}
/* Output
810
*/
注意:这里在运行 main() 方法时一定要指明路径,不能在 Test 类的层次中执行,要指明 Test$Tester 即在 Tester 层次上执行 main() 方法。否则会报错找不到 main() 方法。
6 从多层内部类中访问外部类的成员
class Outer { // 外围类
public class A { // 第一层内部类
public class B { // 第二层内部类
public class C { // 第三层内部类
public void showMessage() { // 属于第三层内部类的方法
System.out.println("Outer.A.B.C.showMessage()");
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
Outer.A oa = o.new A();
Outer.A.B oab = oa.new B();
Outer.A.B.C oabc = oab.new C(); // 必须逐层创建更深层次的内部类类对象
oabc.showMessage();
}
}
/* Output
Outer.A.B.C.showMessage()
*/
解析:当我们要创建嵌套于多层外围类的嵌套类对象时,必须要逐步的创建类对象以得到更深层次的嵌套类,这里借助的 .new 语法。关于 .new 语法,我在内部类的创建与使用中有所提及。
备注
更多细节可以看 Bruce Eckel 所著的《Java 编程思想》,欢迎大家一起学习探讨。