Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482)
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed May 29 14:57:13 UTC 2024
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."
Late to the party. I’m kind of questioning that rule a little bit, to be
honest. More specifically, the text that says that a local/anon class in
a static context has no enclosing instance seems to cast too wide of a
net. If that’s the case, then we can’t really prove (following the rules
in the spec) how references to members in enclosing instances are
resolved. 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| ?
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?
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. Under the hood, this works, because javac captures the
enclosing instance reference of |Inner| and passes that to |Local|. In
other words, it’s as if, from a translation perspective, the enclosing
instance of |Local| was |Outer|. In fact, if we look at the generated
class for |Local| we see this:
|class Outer$Inner$1Local { final Outer val$this$0;
Outer$Inner$1Local(Outer); void test(); } |
Note that there’s no |this$0| field, but |val$this$0| instead. In other
words, it’s as if the |this$0| reference in the |Inner| constructor has
been captured by the local class. While this works out in practice, it
does seem a very roundabout way to say that |Local| indeed has an
enclosing instance (|Outer|) - and javac’s backend trickery of course
doesn’t even explain why the above program is accepted in the first
place. It seems like this area needs a bit of an overhaul? Or am I
missing something?
Cheers
Maurizio
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240529/7f30f0b5/attachment-0001.htm>
More information about the amber-spec-experts
mailing list