为什么jshell允许lambda表达式中使用非final的自由变量?
问题代码复现
class Test { public static void main(String[] args) { int x = 0; Runnable r = () -> System.out.println(x); r.run(); x++; r.run(); } }
常规.java文件编译报错原因
Java 语言规范明确要求:lambda 表达式如果捕获方法内的局部变量,该变量必须是final修饰,或者是有效final(变量赋值后从未被修改)。上述代码中x后续执行了x++修改操作,不满足有效final的要求,因此标准Java编译器会直接抛出编译错误,代码无法正常构建。
JShell中代码正常执行的原因及x的访问逻辑
JShell是Java 9+推出的交互式REPL工具,它的执行环境和常规.java文件的编译执行逻辑有本质区别:
- 你在JShell交互环境中直接定义的顶层变量,本质是JShell内部执行上下文的堆分配成员变量,不是普通方法内栈分配的局部变量,天然不受lambda捕获局部变量的有效final规则限制。
- JShell处理lambda对可变变量的捕获时,不会像常规编译一样拷贝变量的当前值,而是让lambda持有变量的引用。因此第一次调用
r.run()时访问的是初始值0,执行x++修改了同一份引用指向的存储值,第二次调用r.run()自然就能拿到修改后的1。
内容的提问来源于stack exchange,提问作者Mahek Shamsukha
相关产品推荐
相关产品推荐

