Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482)

Archie Cobbs archie.cobbs at gmail.com
Tue Jun 11 15:28:34 UTC 2024


On Tue, Jun 11, 2024 at 3:53 AM Maurizio Cimadamore <
maurizio.cimadamore at oracle.com> wrote:

> So, back to Attr - turns out that this code (of which I’m not very proud
> of :-) ), can be rewritted w/o accessing chk.basicHandler inside the anon
> class.
>

Sure, you could do that - but that's really beside the point.

The point I'm making is simply to show that incidences of classes declared
in early construction contexts accessing outer instances exist out there in
the world. They may be rare, but they are not so rare that they can be
dismissed - at least not according to the high Java/openjdk backward
compatibility standards that I've observed over the years. Do you disagree?


> Thinking more, I think I can perhaps find an argument in defense of the
> strategy of the approach outlined in the current JEP: migrating lambdas to
> classes.
>
> So, back to your example:
>
> class Outer
>     int x;
>     class Inner extends Foo {
>         Inner() {
>             super(new Bar() {
>                 { Outer.this.x++; }   // allowed???
>             });
>         }
>     }
> }
>
> Let’s say we write this using lambdas:
>
>  class Outer
>      int x;
>      class Inner extends Foo {
>          Inner() {
>              super(() -> { ... Outer.this.x++ ... });
>              });
>          }
>      }
>  }
>
> This is now ok, as the lambda has access to any enclosing instances that
> were available at the time the lambda was created (e.g. Outer.this).
>
I'm not sure I understand... I don't see how an inner class vs. a lambda
are any different with respect to how outer instances are handled or can be
accessed. In both cases, the constructor is going to have to construct some
object and pass as a synthetic parameter a copy of the synthetic
"Outer.this" parameter provided to the Inner() constructor. In neither case
can the constructor pass the Inner "this" instance at that time, because it
is still uninitialized.

To be clear, here is how the JDK 17 compiler compiles the two variants of
this class:

import java.util.concurrent.atomic.AtomicReference;
class Outer {
     int x;
     class Inner extends AtomicReference<Runnable> {
         Inner() {
             super(new Runnable() { public void run() { Outer.this.x++; }
});   // variant #1
             super(() ->
Outer.this.x++);                                       // variant #2
         }
     }
 }

Variant #1:

  Outer$Inner(Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LOuter;
       5: aload_0
       6: new           #7                  // class Outer$Inner$1
       9: dup
      10: aload_1
      11: invokespecial #9                  // Method
Outer$Inner$1."<init>":(LOuter;)V
      14: invokespecial #13                 // Method
java/util/concurrent/atomic/AtomicReference."<init>":(Ljava/lang/Object;)V
      17: return

Variant #2:

  Outer$Inner(Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LOuter;
       5: aload_0
       6: aload_1
       7: invokedynamic #7,  0              // InvokeDynamic
#0:run:(LOuter;)Ljava/lang/Runnable;
      12: invokespecial #11                 // Method
java/util/concurrent/atomic/AtomicReference."<init>":(Ljava/lang/Object;)V
      15: return

In both cases, the synthetic Outer.this passed to the Inner() constructor
is in turn passed to the Runnable or lambda. In both cases it would be
illegal to instead access this$0 directly.

-Archie

-- 
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240611/d3358a69/attachment-0001.htm>


More information about the amber-spec-experts mailing list