第十章 内部类(中)
10.7 嵌套类
如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这通常称为嵌套类。普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象,但当内部类是static时就不是这样了,嵌套类意味着:
1、要创建嵌套类的对象,并不需要其外围类的对象
2、不能从嵌套类的对象中访问非静态的外围类对象
嵌套类与普通的内部类还有一个却别,普通内部类的字段与方法只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类,但是嵌套类可以包含所有这些东西:
public class Parcel11 {
private static class ParcelContents implements Contents {
private int i = 11;
public int value() {
return i;
}
}
protected static class ParcelDestination implements Destination {
private String label;
private ParcelDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
public static void f() {}
static int x = 10;
static class AnotherLevel {
public static void f() {};
static int x = 10;
}
}
public static Destination destination(String s) {
return new ParcelDestination(s);
}
public static Contents contents() {
return new ParcelContents();
}
public static void main(String[] args) {
Contents c = contents();
Destination d = destination("Tasmania");
}
}
在main()中,没有任何对Parcel11的对象是必须的,而是选取static成员的普通语法来调用方法。在一个普通非static内部类中,通过一个特殊的this引用就可以链接到其外围对象,嵌套类就没有这个特殊的this引用,这使得它类似于一个static方法。
10.7.1 接口内部的类
正常情况不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,你放到接口中的任何类都是自动地是public和static的,因为类是static的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则,你甚至可以在内部类中实现其外围接口:
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface {
public void howdy() {
System.out.println("Howdy");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
10.7.2 从多层嵌套类中访问外部类的成员
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员:
class MNA {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}
public class MutiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}
可以看到在MNA.A.B中,调用g()和f()不需要任何条件,及时它们被定义为private。
10.8 为什么需要内部类
这里我们已经看到了许多描述内部类的语法和语义,但是不能回答“为什么需要内部类”这个问题。
一般说来,内部类继承自某个类或实现某个接口,内部类代码操作创建它的外围类的对象,所以可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的是,每个内部类都能独立地继承自一个接口的实现,所以无论外围类是否已经继承了某个接口的实现,对于内部类都没有影响。
如果没有内部类提供的,可以继承多个具体的或抽象的类的能力,一些设计与编程的问题就很难解决,从这个角度看,内部类使得多重继承的解决方案变得完整,接口解决了部分问题,而内部类有效实现了多重继承,也就是说,内部类允许继承多个非接口类型。
如果不需要解决多重继承的问题,那么自然可以用别的方式编码,而不需要使用内部类,但如果使用内部类,还可以获得一些其他特性:
1、内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
3、创建内部类对象的时刻并不依赖于外围类对象的创建。
4、内部类并没有令人迷惑的is-a关系,它就是一个独立的实体。
10.8.1 闭包与回调
闭包是一个可调用的对象,它记录了一些来自于创建它的作用域的信息。通过这个定义,可以看出内部类是面向对象的闭包,它不仅包含外围类对象信息,还自动拥有一个指向这个外围类对象的引用,在这个作用域内,内部类有权操作所有的成员,包括private成员。
通过内部类提供闭包的功能是很好的解决方案,它比指针更灵活安全:
interface Incrementable { void increment(); } class Callee1 implements Incrementable { private int i = 0; public void increment() { i++; System.out.println(i); } } class MyIncrement { public void increment() { System.out.println("Other operation"); } static void f(MyIncrement mi) { mi.increment(); } } class Callee2 extends MyIncrement { private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable { public void increment() { Callee2.this.increment(); } } Incrementable getCallbackReference() { return new Closure(); } } class Caller { private Incrementable callbackReference; Caller(Incrementable cbh) { callbackReference = cbh; } void go() { callbackReference.increment(); } } public class Callbacks { public static void main(String[] args) { Callee1 c1 = new Callee1(); Callee2 c2 = new Callee2(); MyIncrement.f(c2); Caller caller1 = new Caller(c1); Caller caller2 = new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); } }
这个例子进一步展示了外围类实现一个接口与内部类实现此接口之间的区别。
10.8.2 内部类与控制框架
应用程序框架就是被设计用以解决某特定问题的一个类或一组类,要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法,在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决我们的特定问题。
模板方法包含算法的基本结构,并且会调用一个或多个可覆盖的方法以完成算法动作。设计模式总是将变化的食物与保持不变的食物分离开,在这个模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。
控制框架是一类特殊的应用程序框架,它用来解决相应时间的需求,主要用来相应时间的系统被称为时间驱动系统。应用程序设计中常见的问题之一是图形用户接口(GUI),它几乎完全是事件驱动的系统。