为什么 java 8 个 lambda 允许访问非最终 class 变量?
Why do java 8 lambdas allow access to non-final class variables?
我明白为什么编译器不接受以下内容:
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 节。仅讨论局部变量)。
没有。它允许访问有效的最终 class 变量。
A variable or parameter whose value is never changed after it is initialized is effectively final.
来源:http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
我们都同意第一个例子不能工作,因为局部变量或参数必须是 final or effectively final to be used within a lambda expression body。
但是你的第二个例子不涉及局部变量或参数,因为str
是一个实例字段。 Lambda 表达式可以像实例方法一样访问实例字段:
A lambda body is either a single expression or a block (§14.2). Like a method body, a lambda body describes code that will be executed whenever an invocation occurs.
事实上,java 编译器从您的 lambda 表达式中创建了一个私有方法 lambda[=13=]
,它只访问实例字段 str
:
private java.lang.String lambda[=10=]() {
0 aload_0; /* this */
1 getfield 14; /* .str */
4 areturn;
}
另一种观点:您也可以使用普通的匿名内部 class:
实现 Supplier
public Supplier<String> makeSupplier() {
return new Supplier<String>() {
public String get() { return str; }
};
}
从内部 classes 访问实例字段非常普遍,而不是 Java 8.
的专长
我明白为什么编译器不接受以下内容:
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 节。仅讨论局部变量)。
没有。它允许访问有效的最终 class 变量。
A variable or parameter whose value is never changed after it is initialized is effectively final.
来源:http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
我们都同意第一个例子不能工作,因为局部变量或参数必须是 final or effectively final to be used within a lambda expression body。
但是你的第二个例子不涉及局部变量或参数,因为str
是一个实例字段。 Lambda 表达式可以像实例方法一样访问实例字段:
A lambda body is either a single expression or a block (§14.2). Like a method body, a lambda body describes code that will be executed whenever an invocation occurs.
事实上,java 编译器从您的 lambda 表达式中创建了一个私有方法 lambda[=13=]
,它只访问实例字段 str
:
private java.lang.String lambda[=10=]() {
0 aload_0; /* this */
1 getfield 14; /* .str */
4 areturn;
}
另一种观点:您也可以使用普通的匿名内部 class:
实现Supplier
public Supplier<String> makeSupplier() {
return new Supplier<String>() {
public String get() { return str; }
};
}
从内部 classes 访问实例字段非常普遍,而不是 Java 8.
的专长