Bad Example
从 lambda 表达式引用的本地变量必须是 final 变量或实际上的 final 变量。
int i = 0;
// compile-time error
// Variable used in lambda expression should be final or effectively final
// 从 lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
foos.forEach(e -> i++);
JLS # Lambda Body
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.
Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.
From 15.27.2. Lambda Body
如果 lambda 里的 本地变量 不是 final 可能会引入并发问题,限制为 final 减少了程序员的负担。
所以这是个 人为限制
,而不是技术上不能实现。
突破限制
在 I know what I'm doing
的前提下,可以突破这层限制,比如 JLS 说的并发问题,那在非并发环境下,就可以去突破下。
单元素数组
int[] i = {0};
foos.forEach(e -> i[0]++);
(匿名)对象封装
// 这里使用的是 java11
var ref = new Object() {
int i = 0;
};
foos.forEach(e -> ref.i++);
AtomicXxx
AtomicInteger i = new AtomicInteger(0);
// AtomicXxx 用对了,那并发环境也不是问题
foos.forEach(e -> i.getAndIncrement());
本质上其实 AtomcXxx
和 (匿名)对象封装
封装是一种,只是前者是 JDK 写好的,后者则需要自定义。