Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482)
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Thu May 30 10:13:15 UTC 2024
Thanks Dan,
I see the new rules. So basically, a local class in a pre-construction
context has an enclosing instance, but then we have a bunch of rules
which say that if I do "Type.this", and I'm in the pre-construction
context for Type, that's an error.
I note that this is effectively equivalent to say that the enclosing
instance of the local class is the first enclosing instance that does
not appear in a pre-construction context, correct? If we stated that,
once and for all (in 8.1.3), couldn't we then avoid the need for having
other special rules?
(e.g. if class L appears in a pre-construction context for C, and C has
enclosing instance D, then, if we say that the enclosing instance of L
is D, automatically C.this becomes ill-formed).
Was this possibility considered, or would it be too problematic to have
an enclosing instance not match the innermost lexically encloisng class?
P.S.
How common is this stuff? This seems to be working today, with some bits
of javac heroics (some bits of which seem to be working mostly by
accident). From the perspective of Java developers, wouldn't it be
easier to say that every class declared in a pre-construction context is
static, period, and cannot reference anyhing from enclosing contexts? I
realize this would be an incompatible change, but it seems to me that
the status quo leaves developers guessing as to whether some code would
compile or not.
Maurizio
On 30/05/2024 05:39, Dan Smith wrote:
>> On May 29, 2024, at 7:57 AM, Maurizio Cimadamore
>> <maurizio.cimadamore at oracle.com> wrote:
>>
>> On 28/05/2024 18:20, Stephan Herrmann wrote:
>>
>>> * §8.1.3: "An instance of an anonymous class whose declaration
>>> occurs in a pre-construction context (8.8.7.1) has no immediately
>>> enclosing instance."
>>
>
> To clarify the model: yes, this rule was intentionally removed. It
> becomes very complicated if you want to make rules about which
> enclosing instances exist or don't exist based on positioning within a
> constructor. Example:
>
> class C1 {
> C1() {
> super();
> class C2 { // not early for C1
> C2() {
> class C3 { // early for C2
> C3() {
> super();
> class C4 { // not early for C3
> ...
> }
> }
> }
> super();
> }
> }
> }
> }
>
> Does C4 have a 1st enclosing instance? Yep. 2nd? Maybe no? 3rd? It
> better...
>
> Instead, the approach we settled on was: early construction contexts
> have no bearing on whether a class "has" an enclosing instance. It
> always does, if it's not in a static context.
>
> Meanwhile, there are a bunch of rules about what you can reference
> from an early construction context *of a particular class*. So, e.g.,
> C4 is in the early construction context of C2, so it is illegal to
> reference the members of C2 in the body of C4.
>
> (Note that this model has nothing to do with how a compiler translates
> these classes into bytecode, which enclosing instances get stored in
> fields, etc. That's a separate problem for javac to deal with.)
>
>> Consider the following example:
>> |class Outer { class Inner { Inner() { class Local { void test() {
>> m(); } } new Local().test(); super(); } } void m() { } public static
>> void main(String[] args) { new Outer().new Inner(); } } |
>>
>> Here, |Local| occurs in an early context (used to be pre-constructor,
>> or static). So it has no enclosing instance. But then, how can it
>> refer to |Outer::m| ?
>>
> Revised rules say that Local does not appear in the early construction
> context *of Outer*, so therefore is allowed to refer to the members of
> Outer.
>
>> In 15.12.1 we say:
>>
>> If there is an enclosing class or interface declaration of which
>> that method is a member, let E be the innermost such class or
>> interface declaration. The type to search is the type of E.this
>> (§15.8.4).
>>
>> Ok, so the above program is valid if |Outer.this| makes sense. But,
>> as per 15.8.4:
>>
>> The value of a qualified this expression TypeName.this is the
>> n’th lexically enclosing instance of this.
>>
>> But there’s no enclosing instance for |Local|, so what does
>> |Outer.this| evaluates to?
>>
> Yep, that's part of what motivated this change. You need to be able to
> talk about the full chain of enclosing instances of Local, even if
> some of them are "unusable".
>
>> I think this has always been a bit of an issue - we always treated
>> anonymous classes occurring in super calls as “static”, but that
>> doesn’t explain how such classes seem to have access to members of
>> enclosing classes.
>>
> Agree, it wasn't well-specified before.
>
>> It seems like this area needs a bit of an overhaul? Or am I missing
>> something?
>>
> Right. The first step was to recognize that an early construction
> context is not the same thing as a static context. The next step was
> to allow enclosing instances for classes in early construction
> contexts. And the third step was to ensure that the restrictions on
> references from early construction contexts continue to apply
> appropriately inside nested classes.
>
> I believe this is now appropriately handled by the spec changes Gavin
> shared, but it's good to validate that with extra eyeballs.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240530/69494727/attachment-0001.htm>
More information about the amber-spec-experts
mailing list