Java中的匿名内部类及其使用技巧

时间:2022-11-18 22:42:56

前言

在介绍匿名内部类之前,首先我们应该先来了解一下内部类及局部内部类:
内部类

内部类(inner class)是定义在另一个类中的类,内部类中的方法可以访问创建该内部类的类(我们称其为外围类 outer class)的域中所有数据(包括私有/private数据)。并且,内部类可以对同一个包中的其他类隐藏起来。

但是由于内部类是一种编译器现象,在虚拟机中只存在常规类文件,所以在编译过程中,内部类被翻译成”外围类名$内部类名”的一个常规类,另一方面由于内部类有非常高的特权(可以访问外围类中的私有域)所以在一些特殊手段下(比如利用十六进制编辑器创建一个虚拟机指令调用该内部类),有可能会导致外围类内的私有域数据泄露。

局部内部类

当某一个类只被唯一一个方法调用时,就可以采用局部内部类来定义它(该类定义在某方法内部)。局部类不能使用public或者private访问说明符进行声明,它的作用域被限定在声明这个局部类的方法(或代码块)中。并且,局部类对外部世界是完全隐藏起来的,及时定义它的方法所在的外围类也不可以访问它。

由于局部内部类对于外围类不可见,所以不存在通过其他手段调用该局部内部类的危险,也就极大的保证了安全性。

正文

匿名内部类定义

假如一个局部内部类只被用一次(只用它构建一个对象),就可以不用对其命名了,这种没有名字的类被称为匿名内部类(anonymous inner class),其代码格式通常为:

new SuperType(construction parameters){
inner class methods and data
};

其中SuperType可以是一个接口(匿名内部类将要实现的接口),也可以是一个类(匿名内部类将要扩展它)。匿名内部类的可见域与局部内部类相同。

匿名内部类的构造器

由于构造器的名字必须与类名相同,而匿名内部类没有类名,所以匿名类不能含有构造器。取而代之的是将构造器参数传递给超类(superclass)的构造器。

当匿名类实现某个接口的时候,一定不能存在任何构造参数。这时,代码格式构成变成如下所示:

new InterfaceType(){
methods and data
};

匿名内部类的注意事项

建立一个与超类类似,但不完全相同的匿名子类非常容易,但这样的子类对象在使用equals方法时要特别当心,我们在定义equals时,一般要对类型进行测试:

if(getClass() != other.getClass()) return false;

这个测试条件在用于匿名子类时会失效!

匿名内部类的使用技巧

这里着重介绍一下双括号初始化(double brace initialization)技巧

假设我们想构造一个数组列表,并将它传给某个方法:

ArrayList<String> friends = new ArrayList<>();
friends.add("Harry");
friends.add("Tony");
invite(friends);

若这个friends数组之后不会再使用的话,我们可以把它构造成一个匿名列表:

invite(new ArrayList<String>(){{add("Harry");add("Tony");}});

这里的两层括号,外层括号建立了一个 ArrayList的匿名子类。内层括号定义了一个该匿名子类的构造块(构造对象时会自动执行的代码块)。

由于匿名内部类的访问特性与局部内部类相同,所以仅仅做了这一个小小的改动不仅保护了数据(如果不是这样做的话,可以用一些特殊方法,得到声明的friends列表)还精简了代码,何乐而不为呢。