lambda表达式中使用的变量应该是final或者有效的final

时间:2016-01-18 22:38:27

标签: java lambda

  

lambda表达式中使用的变量应该是最终的或有效的最终

当我尝试使用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;
}

7 个答案:

答案 0 :(得分:47)

final变量意味着它只能被实例化一次。 在Java中,你不能在lambda和匿名内部类中使用非final变量。

您可以使用旧的for-each循环重构代码:

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
    try {
        for(Component component : cal.getComponents().getComponents("VTIMEZONE")) {
        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;
}

即使我不了解这些代码的某些部分:

  • 您调用v.getTimeZoneId();而不使用其返回值
  • 使用作业calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());,您不会修改最初传递的calTz,并且您不会在此方法中使用它
  • 您总是返回null,为什么不将void设置为返回类型?

希望这些提示可以帮助您改进。

答案 1 :(得分:37)

虽然其他答案证明了这一要求,但他们并没有解释为什么存在这一要求。

JLS在§15.27.2中提到了原因:

  

对有效最终变量的限制禁止访问动态变化的局部变量,其捕获可能会引入并发问题。

为了降低漏洞风险,他们决定确保捕获的变量永远不会发生变异。

答案 2 :(得分:36)

从lambda中,你无法获得任何最终的参考。你需要从lamda外部声明一个最终的包装器来保存你的变量。

我已经添加了最终的'参考'对象作为这个包装器。

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
    final AtomicReference<TimeZone> reference = new AtomicReference<>();

    try {
       cal.getComponents().getComponents("VTIMEZONE").forEach(component->{
        VTimeZone v = (VTimeZone) component;
           v.getTimeZoneId();
           if(reference.get()==null) {
               reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue()));
           }
           });
    } catch (Exception e) {
        //log.warn("Unable to determine ical timezone", e);
    }
    return reference.get();
}   

答案 3 :(得分:30)

Java 8 有一个名为“Effectively final”变量的新概念。这意味着一个非最终的局部变量,其值在初始化后永远不会改变,称为“有效最终”。

引入这个概念是因为在 Java 8 之前,我们不能在anonymous class中使用非最终的局部变量。如果您想访问anonymous class中的本地变量,则必须使其成为最终版本。

引入lambda时,此限制得到了缓解。因此需要制作局部变量final如果它初始化后没有改变,因为lambda本身只是一个匿名类。

每次开发人员使用final时,

Java 8 都意识到声明局部变量lambda的痛苦,并引入了这个概念,并且没有必要创建局部变量final 。因此,如果您发现anonymous class的规则仍然没有更改,那么每次使用final时,您都不必编写lambdas个关键字。

我找到了一个很好的解释here

答案 4 :(得分:6)

在您的示例中,您可以使用简单的forEach循环替换for和lamdba,并自由修改任何变量。或者,可能会重构您的代码,以便您不需要修改任何变量。但是,我会完整地解释错误的含义以及如何解决它。

Java 8语言规范,§15.27.2

  

使用但未在lambda表达式中声明的任何局部变量,形式参数或异常参数必须声明为final或者是有效的final(§4.12.4),否则会在尝试使用时发生编译时错误

基本上,您无法在lambda(或本地/匿名类)中修改局部变量(在本例中为calTz)。要在Java中实现这一点,您必须使用可变对象并从lambda中修改它(通过最终变量)。这里可变对象的一个​​例子是一个元素的数组:

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];
}

答案 5 :(得分:0)

如果没有必要修改变量,那么这种问题的一般解决方法就是 提取使用lambda的代码部分,并在method-parameter上使用final关键字。

答案 6 :(得分:0)

lambda表达式中使用的变量应该是final或有效的final,但是您可以将值分配给final一个元素数组。

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
    try {
        TimeZone calTzLocal[] = new TimeZone[1];
        calTzLocal[0] = calTz;
        cal.getComponents().get("VTIMEZONE").forEach(component -> {
            TimeZone v = component;
            v.getTimeZoneId();
            if (calTzLocal[0] == null) {
                calTzLocal[0] = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
            }
        });
    } catch (Exception e) {
        log.warn("Unable to determine ical timezone", e);
    }
    return null;
}