在Java8之前,我们在匿名内部类中如果使用外部局部变量a,那么变量a必须被声明为final
的。但从Java8开始,我们不需要再去显示地声明这个局部变量为final
了。
同样的代码例子,图一是Java7编译结果,图二是Java8编译结果。
从Java8开始,我们可以在匿名内部类中直接使用非final
变量。不过,这样做是有前提的,就是这个局部变量不能被再被重新赋值!
如果代码中的变量out重新赋值了,会怎么样呢?
无法通过编译。编译提示:变量out
被内部类访问,所以该变量需要定义成final
或者是effectively final
。
所以在这里得出一个结论:在Java8中,内部类访问外部局部变量时,该变量可以不声明为final
的。但是,该变量也不能被重新赋值!所以,在Java中内部类要想访问局部变量,这个变量不可变的原则依然有效。只是,从Java8开始,它不要求程序员在每次使用到这些局部变量的时候都去显式声明为final
。只要该变量不被重新赋值就好了。
再看编译提示,将变量定义成final
或effectively final
。定义成final
,可以理解,就是以前的做法,显示定义成final
,不准重新赋值,保证变量为常量。那effectively final
是什么意思?
effectively final
effectively final
其实是Java8引入的新概念。
一个非final
的局部变量或方法参数,其值在初始化后就从未更改,那么该变量就是effectively final
。这个effectively final
在lambda
表达式上下文中非常有用。
Java8开始,我们可以使用lambda
表达式来编程。lambda
表达式中,使用局部变量的时候,也要求该变量必须是final
的。lambda
表达式在我们的编程中是经常使用到的,而匿名内部类是很少使用的。那么,我们在lambda
编程中每一个被使用到的局部变量都去显示定义成final
吗?显然这不是一个好的方法。所以,Java的设计者们在Java8的版本中,放开了一些限制。
总结一下,规则没有改变,任何局部变量仍然必须是final
的才能在lambda
表达式或匿名内部类中使用,只是你不再需要使用final
关键字,从而节省了一些敲键盘的时间。