![Java无难事:详解Java编程核心思想与技术](https://wfqqreader-1252317822.image.myqcloud.com/cover/59/35011059/b_35011059.jpg)
7.5 匿名内部类
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_22.jpg?sign=1739530197-usYijAhlZWj1bhzLtwdtqXsVPHxgHhbH-0-29d6dcc973c2fa9b0caef02158ca1be7)
扫码看视频
7.5.1 创建匿名内部类
在代码7.9中,我们定义了一个局部内部类MySpeaker,该类只能在getSpeaker方法内部访问,外部访问该类的对象是通过其实现的接口Speaker来访问的。既然如此,这个类有没有名字就不重要了,那么可以将其改造成匿名的内部类。我们看代码7.10。
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_23.jpg?sign=1739530197-APkUh8spQU5yRKxhDtcV9L2gexAsOBzB-0-e251eefe16b126f0814aa70ec2e9ce7b)
注意代码中粗体显示的部分。
“new Speaker(){…};”去掉一对花括号及其中的内容,变成“new Speaker();”,这不就是创建对象的语法吗?然而Speaker是接口,是无法实例化的,需要有接口的实现,于是在“Speaker()”和“;”之间,以一对花括号给出接口的实现,该实现没有类名,其实就是匿名的内部类。
同样,代码7.7也可以改成匿名内部类来实现相同的功能,如代码7.11所示。
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_24.jpg?sign=1739530197-FvsvfLW3JSpfx5gqH8qGqAKccLQK9toV-0-38902fa3369cdd76d0f3ca49f90a2f5c)
一旦掌握了匿名的内部类,可以让你的代码更加简洁清晰。不过要注意的是,匿名内部类一定是一个实现某个接口或者继承某个类的类,所以我们必须在使用匿名内部类之前定义一个接口或者类。如果匿名内部类继承自某个类,那么还有需要注意的地方,如代码7.12所示。
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_25.jpg?sign=1739530197-i7z4CVKSrOtZggN6LJ2Oa90BFrmq9Asg-0-867688e11e62df6a8b69c57b333aeb05)
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_26.jpg?sign=1739530197-UAsknXIonfx8eDvAPUX58ttfv9nRlIYE-0-3d9fe0941c19cae75b1ac8e459d29574)
程序运行的结果为:
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_27.jpg?sign=1739530197-2np377aOmipFgNPvQQke0Ct3MvWFsVH4-0-d801e468f77751061f602abf279f8049)
从运行结果可以看出,调用的getVal方法是匿名内部类的getVal方法,该方法覆盖了基类Desc的getVal方法。但是,当我们试图访问匿名内部类新增的方法cannotAccess时,问题就出现了,编译器告诉我们找不到cannotAccess方法。想想匿名内部类的存在条件:需要实现一个已经声明的接口或者继承某个类,再看看返回匿名内部类对象的方法返回值,其类型已经被限定了,只能是实现的接口或者继承的基类类型,这是一种向上类型转换,匿名内部类新增的方法并不能被加入到已经定义的接口或者基类中。
我们能不能访问这个cannotAccess方法呢?有些读者可能会想到向下类型转换,但是我们没有为这个类起名字,那么要转换成什么类型呢?没错,我们对这个方法确实无能为力。
匿名内部类主要用于创建一个临时的实现某个接口的类,然后返回其对象,所以在使用匿名内部类时考虑的是如何实现接口中声明的方法。当然,我们也可以加入一些其他的辅助方法来帮助完成任务,但是不要想着去加入一些新的希望被用户调用的方法,这没有意义。
使用匿名内部类与使用常规的类相比会有一些限制,虽然匿名内部类可以继承类,也可以实现接口,但是二者只能选择其一,而且当实现接口时,也只能实现一个接口。
7.5.2 匿名内部类的构造方法
匿名内部类本身没有名字,自然也就无法定义自己的构造方法。如果想通过构造方法传递参数,那么只能选择继承某个类,并且该类有带参数的构造方法。我们看代码7.13。
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_28.jpg?sign=1739530197-ljrlLI3DW4lQgbUD77kooESxHAXCyera-0-c4ccce763962325b96aec30965a2e1f2)
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_29.jpg?sign=1739530197-s4OWkrtV9mSxnFWmesI3l5Na4pKSHCpz-0-520c5df84f20538f43fd4584a460de0f)
程序运行结果为:
![img](https://epubservercos.yuewen.com/AD1899/18685354708165706/epubprivate/OEBPS/Images/txt007_30.jpg?sign=1739530197-WplhQOQTVYHEOk3YanOB0Go0jTauZDuV-0-3edf0fe6db0da3b29a1ccb9b0973d9ad)
Desc类有一个构造方法,它接受一个字符串参数。在AnonymousInnerClass 类的getDesc方法中,我们定义了继承自Desc的匿名内部类,在构造内部类对象时调用“new Desc(str)”,向基类的构造方法传递参数。
实际上,方法中的内部类是可以直接访问方法的参数或者局部变量的,因此在绝大多数情况下,匿名的内部类都不需要通过构造方法来传递参数。本例只是用于讲解知识,并无实际意义,完全可以修改为直接访问getDesc方法的参数str,这个交由读者自行完成。
如果确实需要在内部类中添加自定义的构造方法,那么请使用命名的内部类。