Lambda expression using a final field in object initialization block

Jaikiran Pai jai.forums2013 at gmail.com
Wed Mar 6 06:38:24 UTC 2019


Hello Remi,

Thank you for responding :) The reason why I asked this in the jdk-dev
mailing list was a combination of 2 things:

1. I didn't understand why the compiler would want the value to be
initialized at all at that place in the lambda usage and the docs around
this wasn't clear either. I still don't understand why it needs that
field to be initialized whereas its equivalent anonymous class
counterpart doesn't need it to be initialized. With my limited
understanding of lambdas I was under the impression that the members
used in the lambda expression will only be evaluated when the lambda is
invoked, by which time, in this specific case the field will be
initialized since it's a final member of the instance.

2. The mailing list index[1] mentioned this as the place for "Technical
discussion related to the JDK Project".

Having said that, I do respect and understand what you mean by this
being a user/usage question and I'll pursue this further in a different
forum.

[1] https://mail.openjdk.java.net/mailman/listinfo

-Jaikiran

On 04/03/19 6:07 PM, Remi Forax wrote:
> Hi Jaikiran,
> the initialisation block is called before the constructor so at that point name is not initialised, that why your first code fails.
> For the second one, 
>   this.suppliers.put("a", new Supplier<String>() {
>              @Override
>              public String get() {
>                  return name;
>              }
>   });
>
>   "return name" here is equivalent to return LambdaTest.this.name, with LambdaTest.this being the same reference as this inside the initialization block,
>   at that point the compiler doesn't track if name is initialized or not anymore, that's why it compiles. 
>
> so you can fool the compiler the same way with a lambda by introducing a local variable, i.e the example below should compile
>     {
>         var that = this;
>         this.suppliers.put("a", () -> that.name);
>     } 
>
> Note, this mailing list is a list to discuss how to implement the JDK not how to use it.
> This kind of question will be answered better and faster on stackoverflow.com.
>
> regards,
> Rémi
>
> ----- Mail original -----
>> De: "Jaikiran Pai" <jai.forums2013 at gmail.com>
>> À: "jdk-dev" <jdk-dev at openjdk.java.net>
>> Envoyé: Lundi 4 Mars 2019 10:56:28
>> Objet: Lambda expression using a final field in object initialization block
>> Please consider this trivial code:
>>
>>
>> import java.util.Map;
>> import java.util.HashMap;
>> import java.util.function.Supplier;
>>
>> public class LambdaTest {
>>     private final String name;
>>
>>     private final Map<String, Supplier<String>> suppliers = new HashMap<>();
>>
>>     {
>>         this.suppliers.put("a", () -> name);
>>     }
>>
>>     private LambdaTest(final String name) {
>>         this.name = name;
>>     }
>>
>>     public static void main(final String[] args) throws Exception {
>>         final LambdaTest self = new LambdaTest("hello");
>>     }
>> }
>>
>> When I compile this with Java 8 (javac 1.8.0_172) and even Java 11
>> (javac 11.0.1), I get a compilation error, in the object initialization
>> block where I am using a lambda expression to populate the "suppliers"
>> Map and am referring to a final field (called "name") of the class. The
>> compilation error is:
>>
>> LambdaTest.java:12: error: variable name might not have been initialized
>>         this.suppliers.put("a", () -> name);
>>                                       ^
>> 1 error
>>
>> Now, if I replace that piece of code with:
>>
>>
>> import java.util.Map;
>> import java.util.HashMap;
>> import java.util.function.Supplier;
>>
>> public class LambdaTest {
>>     private final String name;
>>
>>     private final Map<String, Supplier<String>> suppliers = new HashMap<>();
>>
>>     {
>>         this.suppliers.put("a", new Supplier<String>() {
>>             @Override
>>             public String get() {
>>                 return name;
>>             }
>>         });
>>     }
>>
>>     private LambdaTest(final String name) {
>>         this.name = name;
>>     }
>>
>>     public static void main(final String[] args) throws Exception {
>>         final LambdaTest self = new LambdaTest("hello");
>>     }
>> }
>>
>> The only change in this is the object initialization block, where I no
>> longer use a lambda and instead use an anonymous class. This passes
>> compilation successfully.
>>
>> Why is it an error to use such a final field in a lambda expression? The
>> (basic) documentation[1] of Lambda expressions doesn't seem to mention
>> this construct as invalid.
>>
>>
>> [1]
>> https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
>>
>> -Jaikiran



More information about the jdk-dev mailing list