The problem is that your two lambdas could be using the value of datamap
before it has been initialized. At least, that is what the JLS definite assignment rules are saying.
This is what will happen when the ConsumerTest
object is created:
- The bare object is allocated.
- The superclass constructor is called (
Object()
) which does nothing. - The instance field declarations are “executed”
dataMap
… not initializedallConsumers
… not initializedevenConsumer
… initialized to the value of the firstlambda
oddConsumer
… initialized to the value of the secondlambda
- The body of the constructor is executed
dataMap
is initializedallConsumers
is initialized.
The problem is (conceptually) this. Those lambdas could in theory be passed anywhere as soon as they have been initialized. If the recipient decides to use them before the dataMap
variable has been initialized, then what would the lambdas see?
- If the
dataMap
variable was notfinal
, then they would see the default initial value; i.e.null
. - In the case where
dataMap
is final, they are not allowed to see anything. ThedataMap
variable must be definitely assigned before it is used … since it isfinal
. The JLS is quite particular on this point.
(Try writing any code that reads a final
variable before it is initialized and you will see what I mean.)
In essence, this is just a rather unexpected (though perfectly logical) consequence of the normal Java rules for final
initialization.
I can think of three solutions in this case:
- Don’t make the
dataMap
variablefinal
. It doesn’t need to be. - Initialize
dataMap
in its declaration rather than in the constructor. - Initialize the lambdas inside the constructor, after initializing
dataMap
. (Ugly … but it would work.)
1
solved Java Lambda Consumer Compilation behavior