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:
mvn clean
followed by mvn compile
.Can someone help me to fix this problem? Or try to reproduce this problem?
1 Java Lambda Expressions. Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. 2 Syntax. Expressions are limited. ... 3 Using Lambda Expressions. Lambda expressions can be stored in variables if the variable's type is an interface which has only one method.
The lambda expression should have the same number of parameters and the same return type as that method. Java has many of these kinds of interfaces built in, such as the Consumer interface (found in the java.util package) used by lists. Use Java's Consumer interface to store a lambda expression in a variable:
So, when we pass a lambda expression to the constructor of Thread class, the compiler tries to convert the expression into equivalent Runnable code as shown in the first code sample. If the compiler succeeds then everything runs fine.
We can declare any type of variable in a Lambda block as similar to declaring variables inside a method. In the below example, creating a lambda expression with local variable localLamdbdaVar. This program compiles and runs with no errors. If we declare the same variable outside lambda like below, it will through compile time error.
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:
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
…
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With