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 firstlambdaoddConsumer… initialized to the value of the secondlambda
- The body of the constructor is executed
dataMapis initializedallConsumersis 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
dataMapvariable was notfinal, then they would see the default initial value; i.e.null. - In the case where
dataMapis final, they are not allowed to see anything. ThedataMapvariable 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
dataMapvariablefinal. It doesn’t need to be. - Initialize
dataMapin 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