Java 8中使用lambdas的意外错误 [英] Unexpected error using lambdas in Java 8

查看:194
本文介绍了Java 8中使用lambdas的意外错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



我使用羊羔如果我切换回匿名类,错误就会消失。



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

  package br; 

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

public class Test {

private final Button button;
私人终端面板;

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]变量面板可能尚未初始化

尽管错误信息很清楚发生了什么(编译器认为最终变量面板在被实例化之前被调用)该变量将不会被调用,直到按钮生成一个动作,并且我们怎么不能说何时发生动作,代码应该编译。实际上,如果我不使用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;
私人终端面板;

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

我注意到与这个问题有关的另外两个奇怪的事情:


  1. Eclipse在自动编译类时不会报告此错误。 Eclipse正在使用与maven相同的JDK来编译该类。

  2. 如果我使用maven编译使用匿名类的类,那么我改变类使用lambdas并再次使用maven进行编译,它不报告错误。在这种情况下,如果我使用 mvn clean ,然后再使用 mvn compile

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

解决方案


尽管错误信息很清楚发生了什么(编译器认为最终变量panel在被实例化之前被调用),直到该按钮生成一个动作为止,该变量才会被调用,并且我们如何不能说何时发生动作,代码应该被编译。


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



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



第16章定义分配




每个局部变量(§14.4)和每个空白最终字段(§4.12.4,§8.3.1.2)必须具有绝对分配的值。



访问其值包括变量的简单名称(或者,对于字段,由限定的字段的简单名称)在表达式中的任何地方发生,除了简单赋值运算符 = (§15.26.1)的左侧操作数之外。




15.27.2。 Lambda Body




...



与匿名类中显示的代码不同声明,名称的含义以及出现在lambda体中的 和 super 关键字以及引用的可访问性声明与周围环境相同(除了lambda参数引入新名称)。


在代码中, lambda体内的名称,读取面板,与构造函数的周围环境相同。在这种情况下,每个空白 final 字段的规则必须在其值发生的任何访问时具有绝对分配的值 p>

而且,这与内部类定义不同。规范明确说明。


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

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

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

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 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. Indeed, it compiles as it should if I don't use 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();
    }
}

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 clean followed by mvn compile.

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

解决方案

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 addActionListener does not invoke the actionPerformed method immediately. It also has no idea about the visibility of the button which determines when actionPerformed might be called.

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

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).

and

15.27.2. Lambda Body

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

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 final field must have a definitely assigned value when any access of its value occurs" applies.

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

这篇关于Java 8中使用lambdas的意外错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆