在开始使用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
虽然错误信息非常清楚正在发生的事情(编译器认为最终变量面板在实例化之前被调用),但是在按钮生成动作之前不会调用变量,以及我们怎么也不能说动作将发生,代码应该编译.实际上,如果我不使用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(); } }
我注意到与此问题相关的其他两个奇怪的事情:
> Eclipse在自动编译类时不会报告此错误. Eclipse使用与maven相同的JDK来编译类.
>如果我使用maven来使用匿名类编译类,那么我将类更改为使用lambdas并再次使用maven编译它,它不会报告错误.在这种情况下,如果我使用mvn clean然后使用mvn compile,它只会再次报告错误.
有人可以帮我解决这个问题吗?或者尝试重现这个问题?
解决方法
Although the error message is pretty clear about what is happening (the compiler thinks the final variable “panel” is being called before it is instantiated),the variable will not be called until the button generates an action,and how we can´t say when the action will happen,the code should compile.
您应该考虑编译器遵循正式规则并且不了解您的事实.特别是,编译器无法知道方法addActionListener不会立即调用actionPerformed方法.它也不知道按钮的可见性,它确定何时可以调用actionPerformed.
正式行为has a specification.在那里你会发现以下几点:
Chapter 16. Definite Assignment
Each local variable (§14.4) and every blank
final
field (§4.12.4,§8.3.1.2) must have a definitely assigned value when any access of its value occurs.An access to its value consists of the simple name of the variable (or,for a field,the simple name of the field qualified by
this
) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator=
(§15.26.1).
和
…
Unlike code appearing in anonymous class declarations,the meaning of names and the
this
andsuper
keywords appearing in a lambda body,along with the accessibility of referenced declarations,are the same as in the surrounding context (except that lambda parameters introduce new names).
在你的代码中,lambda体内名称的含义,面板读取,与构造函数周围的上下文相同.在该上下文中,适用“当其值发生任何访问时,每个空白最终字段必须具有明确赋值的”规则.
而且,是的,这与内部类定义不同.规范明确指出了这一点.