匿名内部类

来源:互联网 发布:学java好还是学ios好 编辑:程序博客网 时间:2024/04/29 22:28
下面的例子看起来有点奇怪:


//: c08:Parcel6.java
// A method that returns an anonymous inner class.


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(); 
  }
} ///:~


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。下面的代码展示的是,如果你
的基类需要一个有参数的构造器,应该怎么办:


//: c08:Parcel7.java
// An anonymous inner class that calls
// the base-class constructor.


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++中是那样)。实际上,它
标记的是表达式的结束,只不过这个表达式正巧包含了内部类罢了。因此,这与别的地方
使用的分号是一致的。


如果在匿名类中定义成员变量,你同样能够对其执行初始化操作:


//: c08:Parcel8.java
// An anonymous inner class that performs
// initialization. A briefer version of Parcel4.java.


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()中的参数。如果你忘记了,会得到一个编译期错误信息。


如果只是简单地给一个成员变量赋值,那么此例中的方法就可以了。但是,如果你想做一
些类似构造器的行为,该怎么办呢?在匿名类中不可能有已命名的构造器(因为它根本没
名字!),但通过实例初始化,你就能够达到为匿名内部类“制作”一个构造器的效果。
像这样做:


//: c08:AnonymousConstructor.java
// Creating a constructor for an anonymous inner class.
import com.bruceeckel.simpletest.*; 


abstract class Base {
public Base(int i) { 
    System.out.println("Base constructor, i = " + i); 
  }
public abstractvoid f();
}
 
public class AnonymousConstructor { 
private static Test monitor = new Test(); 
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();
    monitor.expect(new String[] { 
"Base constructor, i = 47",
"Inside instance initializer",
"In anonymous f()"
    });
  }
} ///:~


在此例中,不要求变量 i 一定是 final 的。因为 i 被传递给匿名类的基类的构造器,它并不
会在匿名类内部被直接使用。


下例是带实例初始化的“parcel”形式。注意 dest()的参数必须是 final,因为它们是在匿
名类内被使用的。


//: c08:Parcel9.java
// Using "instance initialization" to perform
// construction on an anonymous inner class.
import com.bruceeckel.simpletest.*; 


public class Parcel9 {
private static Test monitor = new Test(); 
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); 
    monitor.expect(new String[] { 
"Over budget!"
    });
  }
} ///:~


在实例初始化的部分,你可以看到有一段代码,那原本是不能作为成员变量初始化的一部
分而执行的(就是 if 语句)。所以对于匿名类而言,实例初始化的实际效果就是构造器。

当然它受到了限制:你不能重载实例初始化,所以你只能有一个构造器。