为什么java 8 lambdas允许访问非final类变量?

我理解为什么编译器不接受以下内容:

class Foo { public Supplier makeSupplier() { String str = "hello"; Supplier supp = () -> return str; // gives the expected compile error because // str is not effectively final // (str is a local variable, compile-time error // as per JLS 15.27.2.) str = "world"; return supp; } } 

令我困惑的是编译器接受以下内容,并且unit testing通过:

 class Bar { private String str = "hello"; public void setStr(String str) { this.str = str; } public Supplier makeSupplier() { Supplier supp = () -> { return str; }; return supp; } @Test public void Unit_lambdaCapture() { Supplier supp = makeSupplier(); Assert.assertEquals(supp.get(), "hello"); setStr("foo"); Assert.assertEquals(supp.get(), "foo"); } } 

为什么上述有效且正常工作? 欢迎使用JLS相关部分的指针(第15.27.2节,仅讨论局部变量)。

我们都同意第一个例子不起作用,因为局部变量或参数必须是最终的或有效的最终才能在lambda表达体中使用 。

但是你的第二个例子不涉及局部变量或参数,因为str是一个实例字段。 Lambda表达式可以像实例方法一样访问实例字段:

15.27.2。 Lambda Body

lambda主体是单个表达式或块(第14.2节)。 像方法体一样,lambda体描述了每次调用时都会执行的代码。

事实上,java编译器从lambda表达式中创建一个私有方法lambda$0 ,它只是访问实例字段str

 private java.lang.String lambda$0() { 0 aload_0; /* this */ 1 getfield 14; /* .str */ 4 areturn; } 

另一种观点:您还可以使用普通的匿名内部类来实现Supplier

 public Supplier makeSupplier() { return new Supplier() { public String get() { return str; } }; } 

从内部类访问实例字段非常常见,而不是Java 8的专业。

它不是。 它允许有效地访问最终的类变量。

在初始化之后其值永远不会改变的变量或参数实际上是最终的。

资料来源: http : //docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html