Whereupon, NetBeans flagged an error. I'm guessing this has something to do with the fact that lambdas aren't implemented as anonymous inner classes. Ordinarily, I'd crack the books and seek an answer, but I have a lot on my plate today and would be grateful if anyone who might already know would explain why this is the case.
Here's my code:
Why is there a risk that obj won't be initialized when the lambda code runs, but not when the anonymous inner class code runs. Is there some way the lambda might execute before the constructor returns, but the anonymous inner class's method can't?
It looks to me that you have a final field which has not been initalised in the constructor (nor in an initialiser nor on declaration). That would be a compile‑time error, an hasn't actually got anything to do with the λ. I shall try your code finish my beer, and report back later.
Let's have a look at the JLS: I didn't find anything helpful in the section about constructors.
Campbell's javac tool wrote:Init.java:26: error: variable obj might not have been initialized
Let's try the section about λs. It says that local variables must be effectively final, but that doesn't help because you have already marked the variable final and it is a field not a local variable. The diagram about whether variables have been definitely assigned or not might help a bit. It says that effectively final local variables, etc., must be definitely assigned to before the λ is declared.
Maybe this part of the JLS will help. It says
So every final field must be definitely assigned before it is used. In your code. you are not definitely assigning it until after its use in the λ. The concept of a λ with a variable (which I think constitutes a closure) is that the λ captures the variable with its value. So a λ requires the field be declared with a value before its declaration.
JLS Chapter 16 wrote:Chapter 16. Definite Assignment
Each local variable (§14.4) and every blank final field (§4.12.4, §188.8.131.52) 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).
For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.
Similarly, every blank final variable must be assigned at most once; it must be definitely unassigned when an assignment to it occurs.
What an interesting question
The mystery is why you can get the anonymous class to compile. We know that local variables used in an anonymous class must be declared final (I think that was slightly relaxed in Java8). You are here apparently using a final field before it is assigned to. It must be that the anonymous class assumes the variable will have a value somewhere. If you are going to be strict about definite assignment, you would expect the anonymous class to produce a compiler error too.
Campbell Ritchie wrote:If you are going to be strict about definite assignment, you would expect the anonymous class to produce a compiler error too.
Indeed I would, and here is a reason why:
This code compiles fine but, when the ActionListener is explicitly called from Line 29, the JVM delcares a NullPointerException at Line 23 because, in fact, obj has not then been initialized.
Kind of a double-standard being applied here, eh?
This has me thinking the error message shouldn't be "might not have been initialized," but rather, "has not been initialized." I don't know if a warning for the anonymous inner class would even make sense. Even with final fields, you can get yourself into the same pickle without anonymous inner classes:
There are all kinds of ways that you can access an uninitialized final from within a constructor (further reason to avoid running much code in a constructor). To know if a final might be accessed before initialization would, in the extreme case, call for the compiler to solve The Halting Problem, I think. Apparently, you can use that fact to fool the compiler into compiling something just as flawed as what it won't let you compile:
This compiles fine, but throws another NullPointerException at Line 28 (when it is called from Line 31).
Guess we can only ask the compiler to save us from ourselves, just so much.
So I tried the code on Eclipse, which gives different error messages. Unfortunately not very different; it says
Stevens Miller wrote:. . . a misleading error message. . . .
Sounds like something which require explanation. I think it is a case of a closure capturing the value of the field, so it ought to be initialised before reaching the closure. But the anonymous class simply needs the field to exist and can capture its value when its method is invoked.
The blank final field obj might not have been initialized.
Can't think of anything else to say on this topic. I don't know whether we shall get closer to resolution.