lambda表达式中使用的变量应该是final或有效final
当我尝试使用calTz时,它显示了这个错误。
private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
try {
cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
VTimeZone v = (VTimeZone) component;
v.getTimeZoneId();
if (calTz == null) {
calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
}
});
} catch (Exception e) {
log.warn("Unable to determine ical timezone", e);
}
return null;
}
Java 8有一个叫做“有效最终”变量的新概念。这意味着一个在初始化后值从未改变的非最终局部变量被称为“有效最终”。
引入这个概念是因为在Java 8之前,我们不能在匿名类中使用非最终局部变量。如果你想在匿名类中访问一个局部变量,你必须将它设为final。
引入lambda后,这种限制得到了缓解。因此,如果局部变量在初始化为lambda后没有改变,那么它就需要成为final,因为lambda本身只是一个匿名类。
Java 8意识到了每次开发人员使用lambda时声明局部变量为final的痛苦,引入了这个概念,并使局部变量不再需要为final。因此,如果您看到匿名类的规则没有改变,那么在使用lambdas时,您不必每次都写最后一个关键字。
我在这里找到了一个很好的解释
lambda表达式中使用的变量应该是final或有效的final JAVA
要以不优雅的方式解决这个问题,有两个问题:
副作用和穿线问题
final AtomicInteger e = new AtomicInteger(0);
new Thread(() -> {
e.addAndGet(1);
});
更准确地说,我同意是一种相同的,但使用Lambda函数背后的想法是避免副作用,当我们访问Lambda函数中的最终引用来填充值以从外部获得结果时,我们打破了这个概念。
在最老的帖子中,你可能想这样重写
cal.getComponents () .getComponents (VTIMEZONE) .streams () . map (v - > v.getTimeZoneId () .getValue ()) .collect (Collectors.toList ());
对于线程方面,我们也有同样的副作用问题,此外,你永远不知道什么时候访问原子变量来收集结果,你可以放一个CountDownLatch…最好使用CompletableFuture来处理结果和同步方面
在您的示例中,您可以使用简单的for循环将forEach替换为lamdba,并自由修改任何变量。或者重构代码,这样就不需要修改任何变量。但是,为了完整起见,我将解释这个错误的含义以及如何解决它。
Java 8语言规范,§15.27.2:
在lambda表达式中使用但未声明的任何局部变量、形式形参或异常形参必须声明为final或有效的final(§4.12.4),否则尝试使用时将发生编译时错误。
基本上,您不能在lambda(或本地/匿名类)内修改局部变量(在本例中为calTz)。要在Java中实现这一点,必须使用可变对象并从lambda中修改它(通过final变量)。可变对象的一个例子是一个元素数组:
private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
TimeZone[] result = { null };
try {
cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
...
result[0] = ...;
...
}
} catch (Exception e) {
log.warn("Unable to determine ical timezone", e);
}
return result[0];
}