[External] : Re: This expression in lambda in early construction context

Ella Ananeva ella.ananeva at oracle.com
Wed Jun 5 22:04:23 UTC 2024


Thank you so much!
It makes sense now.

From: Chen Liang <chen.l.liang at oracle.com>
Date: Wednesday, June 5, 2024 at 2:59 PM
To: Ella Ananeva <ella.ananeva at oracle.com>, amber-dev <amber-dev at openjdk.org>
Subject: Re: [External] : Re: This expression in lambda in early construction context
> If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right?

It actually can. In fact, lambdas are just like any objects: wherever the object is accessible, its public methods are accessible, so the lambda can be invoked outside of the constructor if it's passed somewhere else, such as being stored in an instance (non-static) field.

As a result, for now, if you know you executed another method that takes a lambda (like ClassFile.build()) and that lambda is executed, the best bet for you is to declare an array like:

int[] value = new int[0]; // uses a mutable object (here a length-1 array) to capture value
api.execute(arguments, lambdaArg -> { value[0] = lambdaArg; });
this.earlyField = value[0]; // maybe add checks to ensure value is properly initialized

If we just allow assignment to early field in a lambda, it's possible for api to send this lambda into an executor that calls the lambda with 5 second delay; that scenario is clearly not "early-construction context" and the language rule aims to prevent such misuse.

- Chen


________________________________
From: Ella Ananeva <ella.ananeva at oracle.com>
Sent: Wednesday, June 5, 2024 4:29 PM
To: Chen Liang <chen.l.liang at oracle.com>; amber-dev <amber-dev at openjdk.org>
Subject: Re: [External] : Re: This expression in lambda in early construction context


Thanks for pointing this out, Chen.

Please bear with me for a moment. If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right?



class Main {
    int a;
    Main() {
        this.a = 1; //line A
        Foo lmb = () -> this.a = 1;

        lmb.foo(); //line B
        super();
    }



The statement in line A assigns a value to a member field before the superconstructor call.



Lambda also assigns the value to a field and is invoked before the superconstructor call.



I’d say the difference in this behavior makes sense only if the compiler doesn’t attempt to initialize the field in line A but tries to initialize it when the lambda is invoked. It’s like line A is just an instruction for later, and in line B a real initialization attempt takes place.



Is this what is happening?







From: Chen Liang <chen.l.liang at oracle.com>
Date: Wednesday, June 5, 2024 at 1:43 PM
To: Ella Ananeva <ella.ananeva at oracle.com>, amber-dev <amber-dev at openjdk.org>
Subject: Re: [External] : Re: This expression in lambda in early construction context

Hi Ella,

What do you mean by "Why should the rules for a lambda be different"? Lambda bodies are not part of the early-construction context, as the lambda can be invoked anywhere and anytime after it's created, including before the super constructor call completes (such as when it's passed as a parameter to super constructor).



Best,

Chen

________________________________

From: amber-dev <amber-dev-retn at openjdk.org> on behalf of Ella Ananeva <ella.ananeva at oracle.com>
Sent: Wednesday, June 5, 2024 3:30 PM
To: Remi Forax <forax at univ-mlv.fr>; amber-dev <amber-dev at openjdk.org>
Subject: Re: [External] : Re: This expression in lambda in early construction context



Hi Remi,



Good point! I get it that the value of this is not accessible in lambda body before the superconstructor call.

However, we cannot access the value of this in the early construction context even without lambda:



class Test {

    int a;

    Test() {

        this.a = 1;

        int a = this.a; // compilation error

        super();

    }

}



JEP 482 specifically says that in the early construction context the value of this is not accessible, that we only can assigned the value to the fields of this class. Why should the rules for a lambda be different? I’d say, if we want a special behavior for lambda, it should be described in the specification, shouldn’t it?



Thanks,

Ella



From: Remi Forax <forax at univ-mlv.fr>
Date: Wednesday, June 5, 2024 at 12:33 PM
To: Ella Ananeva <ella.ananeva at oracle.com>
Cc: amber-dev <amber-dev at openjdk.org>
Subject: [External] : Re: This expression in lambda in early construction context





________________________________

From: "Ella Ananeva" <ella.ananeva at oracle.com>
To: "amber-dev" <amber-dev at openjdk.org>
Sent: Wednesday, June 5, 2024 8:45:57 PM
Subject: This expression in lambda in early construction context

Hi,

I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression:



class Test {

    int a;

    Test() {

        this.a = 1;

        super();

    }

}

This compiles just fine (I have JDK 23 build 25).

However, when I try to do the same in a lambda body, I get a compilation error:

interface Foo {
void foo();
}

class Test {
int a;

Test() {
Foo lmb = () -> { this.a = 1;};
super();
    }
}

Test.java:9:27

java: cannot reference this before supertype constructor has been called

Is lambda body some special case?



The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor.





Thank you,

Ella Ananeva



regards,

Rémi


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240605/301e7fb5/attachment-0001.htm>


More information about the amber-dev mailing list