我使用的是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();
}
}
我这样编译:
mvn clean;mvn compile
我收到了这个错误:
[ERROR] /C:/Users/fabiano/workspace-luna/Test/src/main/java/br/Test.java:[14,44] variable panel might not have been initialized
虽然错误消息非常清楚正在发生的事情(编译器认为在实例化之前调用了最终变量panel
),但在按钮生成动作之前不会调用该变量,以及如何我们不能说当动作发生时,代码应该编译。事实上,如果我不使用lambdas,它就会编译:
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();
}
}
我注意到另外两个与此问题有关的奇怪事情:
mvn clean
后跟mvn compile
。有人可以帮我解决这个问题吗?或者尝试重现这个问题?
答案 0 :(得分:8)
虽然错误信息非常清楚发生了什么(编译器认为最终变量“panel”在实例化之前被调用),但是在按钮生成动作之前不会调用该变量,我们如何能够要说当动作发生时,代码应该编译。
您应该考虑编译器遵循正式规则并且不了解您的事实。特别是,编译器无法知道方法addActionListener
不会立即调用actionPerformed
方法。它也不知道按钮的可见性,它确定何时可以调用actionPerformed
。
正式行为has a specification。在那里你会发现以下几点:
每个局部变量(第14.4节)和每个空白
final
字段(§4.12.4,§8.3.1.2)在对其值进行任何访问时必须具有明确赋值值对其值的访问包括变量的简单名称(或者,对于字段,由
this
限定的字段的简单名称),在表达式中的任何位置发生,除了左边的操作数简单赋值运算符=
(第15.26.1节)。
和
...
与匿名类声明中出现的代码不同,名称的含义以及出现在lambda主体中的
this
和super
关键字以及引用声明的可访问性与周围的相同上下文(lambda参数引入新名称除外)。
在你的代码中,lambda体内名称的含义,即panel
的读取,与构造函数周围的上下文相同。在该上下文中,“每个空白final
字段在对其值进行任何访问时必须具有明确赋值值的规则”适用。
而且,是的,这与内部类定义不同。规范明确指出。