为什么说 java 中 lambda 表达式的闭包是伪闭包?
Lambda 表达式不依赖于任何外部实体;它们是依赖于自身参数和常量的内容。
public interface calculator{
int calculate(int a);
}
calculator cal = (x)->2*x;
cal.calculate(5);
闭包既依赖于参数和常量,也依赖于它们的词法范围中的变量。
public interface Calculator{
int calculate(int a);
}
int i = 2;
calculator cal = (x)->x*i;
cal.calculate(5);
在Java中,以上两者的概念都被认为是java中的lambda表达式。
在 jdk1.7 中第二个例子中的如果内部类用到了局部变量 i 如果并且不设置为 final 会报编译错误。而在java8 中的语法糖解决了这一点。那我们修改自由变量 i 的值能否通过编译呢。
public interface Calculator{
int calculate(int a);
}
int i = 2;
calculator cal = (x)->x*i;
i++;
cal.calculate(5);
结果是编译失败了,编译器报出了如下错误。
Variable used in lambda expression should be final or effectively final
final 指的是显式得将 i 声明成 final 变量,effectively final 指的是在代码中可以不显式得指明 final 但是你却不能改变他。由此可见 java 8 中只是增加了语法糖,lambda 表达式或者是内部类中仍然不能改变自由变量。
从编译器的角度来看,以上代码其实是被编译成了
class TestClass$1
implements Calculator {
private final TestClass this$0;
private final Integer paramInteger;
TestClass$1
(TestClass this$0, Integer paramInteger) {
this.this$0 = this$0;
this.paramInteger = paramInteger;
}
public Integer calculate(Integer a) {
return a * this.paramInteger;
}
}
i 被作为成员变量传入了编译出的匿名类,因此不管在外部还是内部改变了 i 的值都没有办法影响彼此的值。
从理解角度来看,在代码层面内外部的 i 看上去就是同一个变量,改动彼此都应该生效。但其实不行,因此「 Variable used in lambda expression should be final or effectively final 」。
而在闭包的意义上来看,为了维护自由变量在 lambda 表达式中和外部保持同一状态,既然改变值无法影响状态,干脆就规定只能 final 好了。
因此 java 8 中 lambda 表达式支持的闭包被诟病为一种伪闭包。
参考 [1]: www.zhihu.com/question/24… "闭包(计算机科学)是什么?" [2]: cuipengfei.me/blog/2013/0… "为什么必须是final的呢?" [3]: www.javaworld.com/article/209… "Java programming with lambda expressions" [4]: www.ibm.com/developerwo… "使用闭包捕获状态"