java的匿名内部类
至此,我们已根基领略了内部类的典范用途。对那些涉及内部类的代码,凡是表达的都是“纯真”的内部类,很是简朴,且极易领略。然而,内部类的设计很是全面,不行制止地会碰着它们的其他大量用法——假使我们在一个要领甚至一个任意的浸染域内建设内部类。有两方面的原因促使我们这样做:
(1) 正如前面展示的那样,我们筹备实现某种形式的接口,使本身能建设和返回一个句柄。
(2) 要办理一个巨大的问题,并但愿建设一个类,用来帮助本身的措施方案。同时不肯意把它果真。
在下面这个例子里,将修改前面的代码,以便利用:
(1) 在一个要领内界说的类
(2) 在要领的一个浸染域内界说的类
(3) 一个匿名类,用于实现一个接口
(4) 一个匿名类,用于扩展拥有非默认构建器的一个类
(5) 一个匿名类,用于执行字段初始化
(6) 一个匿名类,通过实例初始化举办构建(匿名内部类不行拥有构建器)
所有这些都在innerscopes包内产生。首先,来自前述代码的通用接口会在它们本身的文件里得到界说,使它们能在所有的例子里利用:
//: Destination.java package c07.innerscopes; interface Destination { String readLabel(); } ///:~
由于我们已认为Contents大概是一个抽象类,所以可采纳下面这种更自然的形式,就象一个接口那样:
//: Contents.java package c07.innerscopes; interface Contents { int value(); } ///:~
尽量是含有详细实施细节的一个普通类,但Wrapping也作为它所有衍生类的一个通用“接口”利用:
//: Wrapping.java package c07.innerscopes; public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } ///:~
在上面的代码中,我们留意到Wrapping有一个要求利用自变量的构建器,这就使环境变得越发有趣了。
第一个例子展示了如安在一个要领的浸染域(而不是另一个类的浸染域)中建设一个完整的类:
//: Parcel4.java // Nesting a class within a method package c07.innerscopes; public class Parcel4 { 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) { Parcel4 p = new Parcel4(); Destination d = p.dest("Tanzania"); } } ///:~
PDestination类属于dest()的一部门,而不是Parcel4的一部门(同时留意可为沟通目次内每个类内部的一个内部类利用类标识符PDestination,这样做不会产生定名的斗嘴)。因此,PDestination不行从dest()的外部会见。请留意在返回语句中产生的上溯造型——除了指向基本类Destination的一个句柄之外,没有任何对象超出dest()的界线之外。虽然,不能由于类PDestination的名字置于dest()内部,就认为在dest()返回之后PDestination不是一个有效的工具。
下面这个例子展示了如安在任意浸染域内嵌套一个内部类:
//: Parcel5.java // Nesting a class within a scope package c07.innerscopes; public class Parcel5 { 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 = ts.getSlip(); } // Can't use it here! Out of scope: //! TrackingSlip ts = new TrackingSlip("x"); } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel5 p = new Parcel5(); p.track(); } } ///:~
TrackingSlip类嵌套于一个if语句的浸染域内。这并不料味着类是有条件建设的——它会伴同其他所有对象获得编译。然而,在界说它的谁人浸染域之外,它是不行利用的。除这些以外,它看起来和一个普通类并没有什么区别。
下面这个例子看起来有些奇怪:
//: Parcel6.java // A method that returns an anonymous inner class package c07.innerscopes; public class Parcel6 { public Contents cont() { return new Contents() { private int i = 11; public int value() { return i; } }; // Semicolon required in this case } public static void main(String[] args) { Parcel6 p = new Parcel6(); Contents c = p.cont(); } } ///:~
#p#分页标题#e#
cont()要领同时归并了返回值的建设代码,以及用于暗示谁人返回值的类。除此以外,这个类是匿名的——它没有名字。并且看起来好像更让人摸不着脑子的是,我们筹备建设一个Contents工具:
return new Contents()
但在这之后,在碰着分号之前,我们又说:“等一等,让我先在一个类界说里再耍一下幻术”:
return new Contents() {
private int i = 11;
public int value() { return i; }
};
这种奇怪的语法要表达的意思是:“建设从Contents衍生出来的匿名类的一个工具”。由new表达式返回的句柄会自动上溯造型成一个Contents句柄。匿名内部类的语法其实要表达的是:
class MyContents extends Contents {
private int i = 11;
public int value() { return i; }
}
return new MyContents();
在匿名内部类中,Contents是用一个默认构建器建设的。下面这段代码展示了基本类需要含有自变量的一个构建器时做的工作:
//: Parcel7.java // An anonymous inner class that calls the // base-class constructor package c07.innerscopes; public class Parcel7 { public Wrapping wrap(int x) { // Base constructor call: return new Wrapping(x) { public int value() { return super.value() * 47; } }; // Semicolon required } public static void main(String[] args) { Parcel7 p = new Parcel7(); Wrapping w = p.wrap(10); } } ///:~
也就是说,我们将适当的自变量简朴地通报给基本类构建器,在这儿表示为在“new Wrapping(x)”中通报x。匿名类不能拥有一个构建器,这和在挪用super()时的通例做法差异。
在前述的两个例子中,分号并不符号着类主体的竣事(和C++差异)。相反,它符号着用于包括匿名类的谁人表达式的竣事。因此,它完全等价于在其他任那里所利用分号。
若想对匿名内部类的一个工具举办某种形式的初始化,此时会呈现什么环境呢?由于它是匿名的,没有名字赋给构建器,所以我们不能拥有一个构建器。然而,我们可在界说本身的字段时举办初始化:
//: 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!"); } 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语句)的一部门执行。所以实际上,一个实例初始化模块就是一个匿名内部类的构建器。虽然,它的成果是有限的;我们不能对实例初始化模块举办过载处理惩罚,所以只能拥有这些构建器的个中一个。