在Java 8中使用lambdas时出现意外错误
我正在使用Java8更新20 32位,Maven 3.2.3,EclipseLuna构建id:20140612-0600 32位 在开始使用lambdas之后,我的项目中的一些类开始在maven中报告编译错误(在Java 8中使用lambdas时出现意外错误,java,eclipse,maven,lambda,java-8,Java,Eclipse,Maven,Lambda,Java 8,我正在使用Java8更新20 32位,Maven 3.2.3,EclipseLuna构建id:20140612-0600 32位 在开始使用lambdas之后,我的项目中的一些类开始在maven中报告编译错误(mvncile) 这些错误仅在使用lambdas时出现。如果我切换回匿名类,错误就会消失 我可以用一个简单的测试用例重现错误: package br; import java.awt.Button; import java.awt.Panel; public class Test {
mvncile
)
这些错误仅在使用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]变量面板可能尚未初始化
虽然错误消息非常清楚发生了什么(编译器认为在实例化前调用了最终变量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();
}
}
我注意到与此问题相关的另外两件奇怪的事情:
mvnclean
后跟mvncile
,它只会再次报告错误addActionListener
不会立即调用actionPerformed
方法。它也不知道按钮的可见性,该按钮决定何时调用actionPerformed
正式的行为。在这里,您可以找到以下几点:
每个局部变量(§14.4)和每个空白final
字段(§4.12.4,§8.3.1.2)在访问其值时必须具有明确的赋值
对其值的访问包括变量的简单名称(或者,对于字段,由this
限定的字段的简单名称),出现在表达式中任何位置,但作为简单赋值运算符=
的左操作数除外(§15.26.1)
及
与匿名类声明中出现的代码不同,lambda正文中出现的名称和this
和super
关键字的含义以及引用声明的可访问性与周围上下文中的含义相同(除了lambda参数引入新名称)
在您的代码中,lambda主体中的名称的含义(读取面板
)与周围上下文中的名称含义相同,后者是构造函数。在这种情况下,适用的规则是“当对其值进行任何访问时,每个空白的final
字段必须有一个明确的赋值”
是的,这与内部类定义不同。规范明确指出。首先,在实例构造期间不应添加侦听器。这会导致引用泄漏,被认为是并发编程中非常糟糕的做法。现在,关于你最后的领域。它显然是在创建引用此面板的侦听器之后初始化的。为什么不能在添加操作侦听器之前初始化面板字段?Panel字段是最终字段,必须在构造函数中进行初始化。@mkrakhin有引用吗?我看不出这会导致泄漏。即使这样,为什么在构造函数中或在另一个方法中会有不同呢?因为最终字段冻结只发生在实例构造结束时。在这种情况下,代码是安全的,至少只要在构造函数中不显示按钮(从而能够被点击)(这无论如何都不应该发生,因为Swing不是线程安全的)@Holger,谢谢你的回答,现在我很清楚lambda与匿名类相比有不同的行为。我认为编译器只是在引擎盖下“替换”了相应匿名类的lambda,行为应该是相同的。我的错。你是对的。我认为,因为lamdba在引擎盖下工作的方式,一个ref参考字段(如面板)实际上是在创建lamdba时访问的。@zeroflagL:这取决于实现,更确切地说是编译器的决定。使用current
javac
测试表明,它在创建时不是访问panel
,而是像任何非final
字段一样读取它。但是其他编译器可能会做不同的事情无论如何,这在Eclipse中对我很有效,直到我使用Maven进行编译才失败