
7.5 匿名内部类

扫码看视频
7.5.1 创建匿名内部类
在代码7.9中,我们定义了一个局部内部类MySpeaker,该类只能在getSpeaker方法内部访问,外部访问该类的对象是通过其实现的接口Speaker来访问的。既然如此,这个类有没有名字就不重要了,那么可以将其改造成匿名的内部类。我们看代码7.10。

注意代码中粗体显示的部分。
“new Speaker(){…};”去掉一对花括号及其中的内容,变成“new Speaker();”,这不就是创建对象的语法吗?然而Speaker是接口,是无法实例化的,需要有接口的实现,于是在“Speaker()”和“;”之间,以一对花括号给出接口的实现,该实现没有类名,其实就是匿名的内部类。
同样,代码7.7也可以改成匿名内部类来实现相同的功能,如代码7.11所示。

一旦掌握了匿名的内部类,可以让你的代码更加简洁清晰。不过要注意的是,匿名内部类一定是一个实现某个接口或者继承某个类的类,所以我们必须在使用匿名内部类之前定义一个接口或者类。如果匿名内部类继承自某个类,那么还有需要注意的地方,如代码7.12所示。


程序运行的结果为:

从运行结果可以看出,调用的getVal方法是匿名内部类的getVal方法,该方法覆盖了基类Desc的getVal方法。但是,当我们试图访问匿名内部类新增的方法cannotAccess时,问题就出现了,编译器告诉我们找不到cannotAccess方法。想想匿名内部类的存在条件:需要实现一个已经声明的接口或者继承某个类,再看看返回匿名内部类对象的方法返回值,其类型已经被限定了,只能是实现的接口或者继承的基类类型,这是一种向上类型转换,匿名内部类新增的方法并不能被加入到已经定义的接口或者基类中。
我们能不能访问这个cannotAccess方法呢?有些读者可能会想到向下类型转换,但是我们没有为这个类起名字,那么要转换成什么类型呢?没错,我们对这个方法确实无能为力。
匿名内部类主要用于创建一个临时的实现某个接口的类,然后返回其对象,所以在使用匿名内部类时考虑的是如何实现接口中声明的方法。当然,我们也可以加入一些其他的辅助方法来帮助完成任务,但是不要想着去加入一些新的希望被用户调用的方法,这没有意义。
使用匿名内部类与使用常规的类相比会有一些限制,虽然匿名内部类可以继承类,也可以实现接口,但是二者只能选择其一,而且当实现接口时,也只能实现一个接口。
7.5.2 匿名内部类的构造方法
匿名内部类本身没有名字,自然也就无法定义自己的构造方法。如果想通过构造方法传递参数,那么只能选择继承某个类,并且该类有带参数的构造方法。我们看代码7.13。


程序运行结果为:

Desc类有一个构造方法,它接受一个字符串参数。在AnonymousInnerClass 类的getDesc方法中,我们定义了继承自Desc的匿名内部类,在构造内部类对象时调用“new Desc(str)”,向基类的构造方法传递参数。
实际上,方法中的内部类是可以直接访问方法的参数或者局部变量的,因此在绝大多数情况下,匿名的内部类都不需要通过构造方法来传递参数。本例只是用于讲解知识,并无实际意义,完全可以修改为直接访问getDesc方法的参数str,这个交由读者自行完成。
如果确实需要在内部类中添加自定义的构造方法,那么请使用命名的内部类。