eclipse 在 Java 8 中使用 lambda 的意外错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25787127/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 21:57:50  来源:igfitidea点击:

Unexpected error using lambdas in Java 8

javaeclipsemavenlambdajava-8

提问by Fabiano

I'm using Java 8 Update 20 32 bits, Maven 3.2.3, Eclipse Luna Build id: 20140612-0600 32 bits.

我正在使用 Java 8 Update 20 32 位、Maven 3.2.3、Eclipse Luna Build id:20140612-0600 32 位。

After starting using lambdas, some classes in my projects started to report compilation errors in maven (mvn compile).

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

These errors appears only when I use lambdas. If I switch back to anonymous classes, the errors are gone.

这些错误仅在我使用 lambda 时出现。如果我切换回匿名类,错误就会消失。

I can reproduce the error with a simple test case:

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

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();
    }
}

I compile it this way:

我是这样编译的:

mvn clean;mvn compile

And I get this error:

我收到这个错误:

[ERROR] /C:/Users/fabiano/workspace-luna/Test/src/main/java/br/Test.java:[14,44] variable panel might not have been initialized

Although the error message is pretty clear about what is happening (the compiler thinks the final variable panelis 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. Indeed, it compiles as it should if I don't use lambdas:

虽然错误信息很清楚发生了什么(编译器认为final变量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();
    }
}

I noticed two other strange things related to this problem:

我注意到与此问题相关的另外两件奇怪的事情:

  1. Eclipse don't report this error when it auto-compile the class. Eclipse is using the same JDK as maven to compile the class.
  2. If I use maven to compile the class using anonymous classes, then I change the class to use lambdas and compile it using maven again, it doesn't report the error. In this case it just reports the error again if I use mvn cleanfollowed by mvn compile.
  1. Eclipse 在自动编译类时不会报告此错误。Eclipse 使用与 maven 相同的 JDK 来编译该类。
  2. 如果我使用 maven 使用匿名类编译类,然后我将类更改为使用 lambdas 并再次使用 maven 编译它,它不会报告错误。在这种情况下,如果我使用mvn clean后跟mvn compile.

Can someone help me to fix this problem? Or try to reproduce this problem?

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

回答by Holger

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.

虽然错误信息很清楚发生了什么(编译器认为最终变量“面板”在实例化之前被调用了),但是直到按钮生成一个动作时才会调用该变量,而且我们不能说当动作发生时,代码应该编译。

You should consider the fact that compilers follow formal rules and don't have your knowledge. Especially, a compiler can't know that the method addActionListenerdoes not invoke the actionPerformedmethod immediately. It also has no idea about the visibility of the button which determines when actionPerformedmight be called.

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

The formal behavior has a specification. There you find the following points:

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

Chapter 16. Definite Assignment

第 16 章明确赋值

Each local variable (§14.4) and every blank finalfield (§4.12.4, §8.3.1.2) must have a definitely assignedvalue 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).

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

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

and

15.27.2. Lambda Body

15.27.2. Lambda 体

Unlike code appearing in anonymous class declarations, the meaning of names and the thisand superkeywords 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).

出现在匿名类的声明不同的代码,名称的含义和thissuper出现在拉姆达身体,用引用声明的可访问性以及关键字,都一样在周围环境(除了拉姆达参数引入新的名称)。

In your code, the meaning of names within the lambda body, read of panel, is the same as in the surrounding context which is the constructor. In that context, the rule that “every blank finalfield must have a definitely assignedvalue when any access of its value occurs” applies.

在您的代码中, lambda 主体中名称的含义(读取 of )panel与周围上下文(即构造函数)中的含义相同。在这种情况下,“当对其值进行任何访问时,每个空白final字段都必须具有明确分配的值”的规则适用。

And, yes, that's different from inner class definitions. The specification explicitly states that.

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