下面的例子看起来有点奇怪: //在方法中返回一个匿名内部类 public class Parcel6 { public Contents cont() { return new Contents() { private int i = 11;
public int value() { return i; } }; // 在这里需要一个分号 }
public static void main(String[] args) { Parcel6 p = new Parcel6(); Contents c = p.cont(); } } cont()方法将下面两个动作合并在一起:返回值的生成,与表示这个返回值的类的定义!进一步说,这个类是匿名的,它没有名字。更糟的是,看起来是你正要创建一个Contents对象: return new Contents() 但是,在到达语句结束的分号之前,你却说:“等一等,我想在这里插入一个类的定义”: return new Contents() { private int i = 11; public int value() { return i; } }; 这种奇怪的语法指的是:“创建一个继承自Contents的匿名类的对象。”通过new 表达式返回的引用被自动向上转型为对Contents的引用。匿名内部类的语法是下面例子的简略形式: class MyContents implements Contents { private int i = 11; public int value() { return i; } } return new MyContents(); 在这个匿名内部类中,使用了缺省的构造器来生成Contents。下面的代码展示的是,如果你的基类需要一个有参数的构造器,应该怎么办: public class Parcel7 { public Wrapping wrap(int x) { // Base constructor call: return new Wrapping(x) { // Pass constructor argument. public int value() { return super.value() * 47; } }; // Semicolon required } public static void main(String[] args) { Parcel7 p = new Parcel7(); Wrapping w = p.wrap(10); } } 只需简单地传递合适的参数给基类的构造器即可,这里是将x 传进new Wrapping(x)。在匿名内部类末尾的分号,并不是用来标记此内部类结束(C++中是那样)。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了内部类罢了。因此,这与别的地方使用的分号是一致的。
如果在匿名类中定义成员变量,你同样能够对其执行初始化操作: 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()中的参数。如果你忘记了,会得到一个编译期错误信息。如果只是简单地给一个成员变量赋值,那么此例中的方法就可以了。但是,如果你想做一些类似构造器的行为,该怎么办呢?在匿名类中不可能有已命名的构造器(因为它根本没名字!),但通过实例初始化,你就能够达到为匿名内部类“制作”一个构造器的效果。像这样做: abstract class Base { public Base(int i) { System.out.println("Base constructor, i = " + i); } public abstract void f(); }
public class AnonymousConstructor { public static Base getBase(int i) { return new Base(i) { { System.out.println("Inside instance initializer"); } public void f() { System.out.println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase(47); base.f(); } } 在此例中,不要求变量i 一定是final 的。因为i 被传递给匿名类的基类的构造器,它并不会在匿名类内部被直接使用。下例是带实例初始化的“parcel”形式。注意dest()的参数必须是final,因为它们是在匿名类内被使用的。 public class Parcel9 { public Destinationdest(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 语句)。所以对于匿名类而言,实例初始化的实际效果就是构造器。当然它受到了限制:你不能重载实例初始化,所以你只能有一个构造器。
从多层嵌套类中访问外部 一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员,如下所示: class MNA { private void f() {} class A { private void g() {} public class B { void h() { g(); f(); } } } } public class MultiNestingAccess { 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)。这个例子同时展示了如何从不同的类里面创建多层嵌套的内部类对象的基本语法。“.new”语法能产生正确的作用域,所以你不必在调用构造器时限定类名。
内部类的继承问题(thinking in java 3th p294) 因为内部类的构造器要用到其外围类对象的引用,所以在你继承一个内部类的时候,事情变得有点复杂。问题在于,那个“秘密的”外围类对象的引用必须被初始化,而在被继承的类中并不存在要联接的缺省对象。要解决这个问题,需使用专门的语法来明确说清它们之间的关联: class WithInner { class Inner { Inner(){ System.out.println("this is a constructor in WithInner.Inner"); }; } }
public class InheritInner extends WithInner.Inner { // ! InheritInner() {} // Won"t compile InheritInner(WithInner wi) { wi.super(); System.out.println("this is a constructor in InheritInner"); }
public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } }
输出结果为: this is a constructor in WithInner.Inner this is a constructor in InheritInner