Java 匿名内部类,与前面介绍的内部类不同,匿名内部类没有单独的类定义,它在创建对象的同时定义类,语法如下:
new 父类(参数列表) {
//匿名内部类实现部分
}
或者
new 父接口() {
//匿名内部类实现部分
}
匿名内部类是与new关联的,在创建对象的时候定义类,new后面是父类或者父接口,然后是圆括号()
,里面可以是传递给父类构造方法的参数,最后是大括号{}
,里面是类的定义。
public class Outer {
public void test(final int x, final int y) {
Point p = new Point(2, 3) {
@Override
public double distance() {
return distance(new Point(x, y));
}
};
System.out.println(p.distance());
}
}
创建Point对象的时候,定义了一个匿名内部类,这个类的父类是Point,创建对象的时候,给父类构造方法传递了参数2和3,重写了distance()方法,在方法中访问了外部方法final参数x和y。
匿名内部类只能被使用一次,用来创建一个对象。它没有名字,没有构造方法,但可以根据参数列表,调用对应的父类构造方法。它可以定义实例变量和方法,可以有初始化代码块,初始化代码块可以起到构造方法的作用,只是构造方法可以有多个,而初始化代码块只能有一份。因为没有构造方法,它自己无法接受参数,如果必须要参数,则应该使用其他内部类。与方法内部类一样,匿名内部类也可以访问外部类的所有变量和方法,可以访问方法中的final参数和局部变量。
匿名内部类是怎么实现的呢?每个匿名内部类也都被生成为一个独立的类,只是类的名字以外部类加数字编号,没有有意义的名字。上述代码会产生两个类Outer和Outer$1
,代码如下所示。
public class Outer {
public void test(final int x, final int y){
Point p = new Outer$1(this,2,3, x, y);
System.out.println(p.distance());
}
}
public class Outer$1 extends Point {
int x2;
int y2;
Outer outer;
Outer$1(Outer outer, int x1, int y1, int x2, int y2){
super(x1, y1);
this.outer = outer;
this.x2 = x2;
this.y2 = y2;
}
@Override
public double distance() {
return distance(new Point(this.x2, y2));
}
}
与方法内部类类似,外部实例this、方法参数x和y都作为参数传递给了内部类构造方法。此外,new时的参数2和3也传递给了构造方法,内部类构造方法又将它们传递给了父类构造方法。
匿名内部类能做的,方法内部类都能做。但如果对象只会创建一次,且不需要构造方法来接受参数,则可以使用匿名内部类,这样代码书写上更为简洁。
在调用方法时,很多方法需要一个接口参数,比如Arrays.sort
方法,它可以接受一个数组,以及一个Comparator接口参数,Comparator有一个方法compare用于比较两个对象。比如,要对一个字符串数组不区分大小写排序,可以使用Arrays.sort
方法,但需要传递一个实现了Comparator接口的对象,这时就可以使用匿名内部类,代码如下所示:
public void sortIgnoreCase(String[] strs){
Arrays.sort(strs, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
}
Comparator后面的<String>
与泛型有关,表示比较的对象是字符串类型。匿名内部类还经常用于事件处理程序中,用于响应某个事件,比如一个Button,处理单击事件的代码可能类似如下:
Button bt = new Button();
bt.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
//处理事件
}
});
调用addActionListener将事件处理程序注册到了Button对象bt中,当事件发生时,会调用actionPerformed方法,并传递事件详情ActionEvent作为参数。
以上Arrays.sort
和Button都是针对接口编程的例子,另外,它们也都是一种回调的例子。所谓回调是相对于一般的正向调用而言的,平时一般都是正向调用,但Arrays.sort中传递的Comparator对象,它的compare方法并不是在写代码的时候被调用的,而是在Arrays. sort
的内部某个地方回过头来调用的。Button的addActionListener中传递的ActionListener对象,它的actionPerformed方法也一样,是在事件发生的时候回过头来调用的。
将程序分为保持不变的主体框架,和针对具体情况的可变逻辑,通过回调的方式进行协作,是计算机程序的一种常用实践。匿名内部类是实现回调接口的一种简便方式。
评论前必须登录!
注册