为什么Java8Lambda允许访问非最终类变量?
我理解为什么编译器不接受以下内容:
class Foo {
public Supplier<String> makeSupplier() {
String str = "hello";
Supplier<String> 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;
}
}
让我困惑的是,编译器接受以下内容,而单元测试通过了:
class Bar {
private String str = "hello";
public void setStr(String str) {
this.str = str;
}
public Supplier<String> makeSupplier() {
Supplier<String> supp = () -> { return str; };
return supp;
}
@Test
public void Unit_lambdaCapture() {
Supplier<String> supp = makeSupplier();
Assert.assertEquals(supp.get(), "hello");
setStr("foo");
Assert.assertEquals(supp.get(), "foo");
}
}
为什么上述方法有效且工作正常?欢迎指向JLS相关章节的指针(第15.27.2节。仅讨论局部变量)
# 1 楼答案
我们都同意第一个例子不能作为局部变量使用,或者参数必须是final or effectively final才能在lambda expression body中使用
但是您的第二个示例不涉及局部变量或参数,因为
str
是一个实例字段。Lambda表达式可以使用与实例方法相同的方式访问实例字段:15.27.2. Lambda Body
事实上,java编译器从lambda表达式中创建了一个私有方法
lambda$0
,它只访问实例字段str
:另一种观点:您还可以使用普通的旧匿名内部类实现
Supplier
:从内部类访问实例字段是非常常见的,而不是Java8的特长
# 2 楼答案
事实并非如此。它允许访问有效的最终类变量
资料来源:http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html