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 initialized
- allConsumers… not initialized
- evenConsumer… initialized to the value of the first- lambda
- oddConsumer… initialized to the value of the second- lambda
 
- The body of the constructor is executed
- dataMapis initialized
- allConsumersis 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