Forward references in initializers
Dan Smith
daniel.smith at oracle.com
Thu Nov 7 20:36:43 UTC 2024
I've been looking with Vicente and Ella at the rules for forward references in field/block initializers. I originally specified it in the JEP 401 language spec to maximize expressiveness, but I'm now thinking we'd be better off sticking with the current left-to-right treatment.
Here's a test:
value class ValueTest {
{ System.out.println(x); }
int x = "abc".length();
}
The JEP 401 spec rules say this is allowed, because 'x' has an early initializer, and the block executes late. That is, the <init> method looks like:
x = "abc".length();
super();
{ System.out.println(x); }
This could also manifest with null-restricted types:
class NullTest {
int x = y.length();
String! y = "abc".substring(1);
}
Which might compile to:
y = "abc".substring(1);
super();
x = y.length();
Note that we could actually do something similar with constant fields today, because constant fields are initialized before anything else:
class StaticTest {
static int x = y.length();
static final String y = "abc";
}
But we don't: the rule simply says that forward references are illegal, regardless of the timing of initialization at run time.
Reflecting on this, I think all of ValueTest, NullTest, and StaticTest should be errors.
I like that, under the existing left-to-right rule, a reader can make sense of the initialization code, *as if* it were all run at once, left to right. In the rare cases that timing/side effects matter, a more sophisticated reader would need to understand that some initializers run early, and others run late. But because of the restrictions on early construction code (no field reads), we can leave the left-to-right reading as "good enough" for most developers, and stick with language rules that reinforce this.
More information about the valhalla-spec-experts
mailing list