局部变量必须显式声明为final
lambda表达式对局部变量的限制 - 图1

原因

  1. public class LambdaTest {
  2. long l = 1L;
  3. private void test() {
  4. String s = "aaa";
  5. Object localObj2 = new Object();
  6. Integer a = 0;
  7. Consumer consumer = (x) -> {
  8. System.out.println(x);
  9. System.out.println(l);
  10. System.out.println(s);
  11. System.out.println(a);
  12. };
  13. consumer.accept(localObj2);
  14. }
  15. }

之后编译该类,反编译字节码:

  1. javac src/LambdaTest.java
  2. javap -p src/LambdaTest.class
  3. 输出:
  4. Compiled from "LambdaTest.java"
  5. public class LambdaTest {
  6. long l;
  7. public LambdaTest();
  8. private void test();
  9. private void lambda$test$0(java.lang.String, java.lang.Integer, java.lang.Object);
  10. }

相当于

  1. long l = 1L;
  2. private void test() {
  3. String s = "aaa";
  4. Object localObj2 = new Object();
  5. Integer a = 0;
  6. testlambda(localObj2,s,a);
  7. }
  8. private void testlambda(Object x,String s,Integer a){
  9. System.out.println(x);
  10. System.out.println(this.l);
  11. System.out.println(s);
  12. System.out.println(a);
  13. }

由此可看出,Lambda表达式是一个语法糖,会被编译生成为当前类的一个私有方法,Lambda表达式内直接引用局部变量本质是一种隐式传参编译时会自动将引用的局部变量放到参数列表中(Lambda方法多了个参数),而引用的实例变量并不需要放到参数列表,因为方法内可以直接引用。
所以在Lambda中对参数重新赋值或者在方法中将局部变量重新赋值,对另一方都是没有影响的。
因此,为了避免这种误导混淆,保证局部变量和Lambda的变量副本的数据一致性,Java直接在语法层面强制Lambda表达式引用的局部变量不可被重新赋值