在Java 8中使用lambdas时出现意外错误

Fab*_*ano 7 java eclipse lambda maven java-8

我使用的是Java 8 Update 20 32位,Maven 3.2.3,Eclipse Luna Build id:20140612-0600 32位.

在开始使用lambdas之后,我的项目中的一些类开始报告maven(mvn compile)中的编译错误.

只有在我使用lambdas时才会出现这些错误.如果我切换回匿名类,错误就消失了.

我可以通过一个简单的测试用例重现错误:

package br;

import java.awt.Button;
import java.awt.Panel;

public class Test {

    private final Button button;
    private final Panel panel;

    public Test() {
        button = new Button();
        button.addActionListener(event -> {
            System.out.println(panel);
        });
        panel = new Panel();
    }
}
Run Code Online (Sandbox Code Playgroud)

我这样编译:

mvn clean;mvn compile
Run Code Online (Sandbox Code Playgroud)

我收到这个错误:

[ERROR] /C:/Users/fabiano/workspace-luna/Test/src/main/java/br/Test.java:[14,44] variable panel might not have been initialized
Run Code Online (Sandbox Code Playgroud)

虽然错误信息非常清楚发生了什么(编译器认为最终变量panel在实例化之前被调用),但是在按钮生成动作之前不会调用变量,以及我们怎么也不能说动作何时会发生,代码应该编译.实际上,如果我不使用lambda,它会按照它应该编译:

package br;

import java.awt.Button;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Test {

    private final Button button;
    private final Panel panel;

    public Test() {
        button = new Button();
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println(panel);
            }
        });
        panel = new Panel();
    }
}
Run Code Online (Sandbox Code Playgroud)

我注意到与此问题相关的其他两个奇怪的事情:

  1. Eclipse在自动编译类时不会报告此错误.Eclipse使用与maven相同的JDK来编译类.
  2. 如果我使用maven来使用匿名类编译类,那么我将类更改为使用lambdas并再次使用maven编译它,它不会报告错误.在这种情况下,如果我使用mvn clean后面的话,它只会再次报告错误mvn compile.

有人可以帮我解决这个问题吗?或者尝试重现这个问题?

Hol*_*ger 9

虽然错误消息非常清楚正在发生的事情(编译器认为最终变量"panel"在实例化之前被调用),但是在按钮生成动作之前不会调用该变量,以及我们怎么说不能当动作发生时,代码应该编译.

您应该考虑编译器遵循正式规则并且不了解您的事实.特别是,编译器无法知道该方法addActionListener不会actionPerformed立即调用该方法.它也不知道确定何时actionPerformed可以调用的按钮的可见性.

正式行为有一个规范.在那里你会发现以下几点:

第16章.明确的分配

当对其值进行任何访问时final,每个局部变量(第14.4节)和每个空白字段(第4.12.4节,第8.3.1.2节)必须具有明确赋值.

对其值的访问包括变量的简单名称(或者,对于字段,限定字段的简单名称this),除了作为简单赋值运算符的左手操作数=(第15.26节)外,它出现在表达式的任何位置. 1).

15.27.2.Lambda Body

...

与出现在匿名类的声明,名称的含义和代码this,并super在一个lambda身体出现关键词,与引用声明的可访问性一起,是同周围环境(除了拉姆达参数引入新的名称).

在您的代码中,lambda主体中名称的含义(读取panel)与构造函数的周围上下文中的含义相同.在该上下文中,适用"当其值发生任何访问时,每个空白final字段必须具有明确赋值的 "规则.

而且,是的,这与内部类定义不同.规范明确指出了这一点.