至此我们已基本理解了内部类的典型用途对那些涉及内部类的代码通常表达的都是单纯的内部类非常简单且极易理解然而内部类的设计非常全面不可避免地会遇到它们的其他大量用法——假若我们在一个方法甚至一个任意的作用域内创建内部类有两方面的原因促使我们这样做 () 正如前面展示的那样我们准备实现某种形式的接口使自己能创建和返回一个句柄 () 要解决一个复杂的问题并希望创建一个类用来辅助自己的程序方案同时不愿意把它公开 在下面这个例子里将修改前面的代码以便使用 () 在一个方法内定义的类 () 在方法的一个作用域内定义的类 () 一个匿名类用于实现一个接口 () 一个匿名类用于扩展拥有非默认构建器的一个类 () 一个匿名类用于执行字段初始化 () 一个匿名类通过实例初始化进行构建(匿名内部类不可拥有构建器) 所有这些都在innerscopes包内发生首先来自前述代码的通用接口会在它们自己的文件里获得定义使它们能在所有的例子里使用 //: Destinationjava package cinnerscopes; interface Destination { String readLabel(); } ///:~ 由于我们已认为Contents可能是一个抽象类所以可采取下面这种更自然的形式就象一个接口那样 //: Contentsjava package cinnerscopes; interface Contents { int value(); } ///:~ 尽管是含有具体实施细节的一个普通类但Wrapping也作为它所有衍生类的一个通用接口使用 //: Wrappingjava package cinnerscopes; public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } ///:~ 在上面的代码中我们注意到Wrapping有一个要求使用自变量的构建器这就使情况变得更加有趣了 第一个例子展示了如何在一个方法的作用域(而不是另一个类的作用域)中创建一个完整的类 //: Parceljava // Nesting a class within a method package cinnerscopes; public class Parcel { public Destination dest(String s) { class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new PDestination(s); } public static void main(String[] args) { Parcel p = new Parcel(); Destination d = pdest(Tanzania); } } ///:~ PDestination类属于dest()的一部分而不是Parcel的一部分(同时注意可为相同目录内每个类内部的一个内部类使用类标识符PDestination这样做不会发生命名的沖突)因此PDestination不可从dest()的外部访问请注意在返回语句中发生的上溯造型——除了指向基础类Destination的一个句柄之外没有任何东西超出dest()的边界之外当然不能由于类PDestination的名字置于dest()内部就认为在dest()返回之后PDestination不是一个有效的对象 下面这个例子展示了如何在任意作用域内嵌套一个内部类 //: Parceljava // Nesting a class within a scope package cinnerscopes; public class Parcel { private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip(slip); String s = tsgetSlip(); } // Cant use it here! Out of scope: //! TrackingSlip ts = new TrackingSlip(x); } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel p = new Parcel(); ptrack(); } } ///:~ TrackingSlip类嵌套于一个if语句的作用域内这并不意味着类是有条件创建的——它会随同其他所有东西得到编译然而在定义它的那个作用域之外它是不可使用的除这些以外它看起来和一个普通类并没有什么区别 下面这个例子看起来有些奇怪 //: Parceljava // A method that returns an anonymous inner class package cinnerscopes; public class Parcel { public Contents cont() { return new Contents() { private int i = ; public int value() { return i; } }; // Semicolon required in this case } public static void main(String[] args) { Parcel p = new Parcel(); Contents c = nt(); } } ///:~ cont()方法同时合并了返回值的创建代码以及用于表示那个返回值的类除此以外这个类是匿名的——它没有名字而且看起来似乎更让人摸不着头脑的是我们准备创建一个Contents对象 return new Contents() 但在这之后在遇到分号之前我们又说等一等让我先在一个类定义里再耍一下花招 return new Contents() { private int i = ; public int value() { return i; } }; 这种奇怪的语法要表达的意思是创建从Contents衍生出来的匿名类的一个对象由new表达式返回的句柄会自动上溯造型成一个Contents句柄匿名内部类的语法其实要表达的是 class MyContents extends Contents { private int i = ; public int value() { return i; } } return new MyContents(); 在匿名内部类中Contents是用一个默认构建器创建的下面这段代码展示了基础类需要含有自变量的一个构建器时做的事情 //: Parceljava // An anonymous inner class that calls the // baseclass constructor package cinnerscopes; public class Parcel { public Wrapping wrap(int x) { // Base constructor call: return new Wrapping(x) { public int value() { return supervalue() * ; } }; // Semicolon required } public static void main(String[] args) { Parcel p = new Parcel(); Wrapping w = pwrap(); } } ///:~ 也就是说我们将适当的自变量简单地传递给基础类构建器在这儿表现为在new Wrapping(x)中传递x匿名类不能拥有一个构建器这和在调用super()时的常规做法不同 在前述的两个例子中分号并不标志着类主体的结束(和C++不同)相反它标志着用于包含匿名类的那个表达式的结束因此它完全等价于在其他任何地方使用分号 若想对匿名内部类的一个对象进行某种形式的初始化此时会出现什么情况呢?由于它是匿名的没有名字赋给构建器所以我们不能拥有一个构建器然而我们可在定义自己的字段时进行初始化 //: Parceljava // An anonymous inner class that performs // initialization A briefer version // of Parceljava package cinnerscopes; public class Parcel { // 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) { Parcel p = new Parcel(); Destination d = pdest(Tanzania); } } ///:~ 若试图定义一个匿名内部类并想使用在匿名内部类外部定义的一个对象则编译器要求外部对象为final属性这正是我们将dest()的自变量设为final的原因如果忘记这样做就会得到一条编译期出错提示 只要自己只是想分配一个字段上述方法就肯定可行但假如需要采取一些类似于构建器的行动又应怎样操作呢?通过Java 的实例初始化我们可以有效地为一个匿名内部类创建一个构建器 //: Parceljava // Using instance initialization to perform // construction on an anonymous inner class package cinnerscopes; |