7.4 局部内部类

img

扫码看视频

还可以在方法中定义内部类,甚至在语句块中也可以定义内部类,这种情况通常是某个方法需要创建一个类来辅助完成该方法的功能,而这个类只用在该方法中。局部内部类需要配合接口来一起使用。我们看一个例子,如代码7.8所示。

img
img

局部内部类限定了该类只能在局部作用域内被访问,因此是无法直接返回该类型的对象的,需要向上转型为其实现的接口类型。此外,局部内部类不能使用public和private访问说明符进行声明

程序的运行结果是:

img

在本例中,局部内部类MySpeaker有一个实例变量str,其值是方法getSpeaker的形参str的值,在构造内部类对象时,通过new MySpeaker(str)传入进去。实际上,在局部内部类中可以直接访问方法的参数,这样就可以减少内部类的实例变量数。修改代码7.8,删除内部类MySpeaker的实例变量str,改为直接访问getSpeaker方法的参数,如代码7.9所示。

img

可以看到,代码变得更加简洁了。

我们来分析一下main方法中的调用过程:

(1)创建LocalInnerClass类的对象lic。

(2)调用lic的getSpeaker方法,传入实参“Local inner class”,方法形参str有值了。

(3)getSpeaker方法返回Speaker接口类型的对象,该方法结束,清理方法所在的栈空间,形参str被清理。

(4)调用Speaker对象的speak方法,输出getSpeaker方法的形参str的值。

嗯?形参str不是被清理了,不复存在了吗?

实际上,早期版本的JDK要求局部内部类在访问本地变量时(方法形参或方法内部定义的局部变量),该本地变量必须声明为final,而我们知道final代表的是“最终的”“不可修改的”,这样该变量就变成了常量,并被保存到常量池中,简单来说,就是改变了本地变量的生存时间。背后的实现就是在编译后的内部类中自动生成了一个名字为“val$变量名”的final实例变量,然后在内部类对象创建时,将方法的参数传递给构造方法,用于初始化这个final常量。之后内部类对象访问的都是自己的这个final常量。

从Java 8开始,已经不要求将本地变量必须声明为final,不过其背后的实现原理是一样的,这些工作都是由编译器来完成的。