Loosening requirements for super() invocation
Brian Goetz
brian.goetz at oracle.com
Sat Dec 31 19:02:27 UTC 2022
> Let's say we have a constructor that assigns all the member fields and then leaks "this". Would this be flagged? If so, why? This doesn't seem to be a leak any more since the object is fully constructed.
This is a common misconception, but there are at least two serious problems with this theory.
First, if the class is not final, then it may not be fully constructed. Consider:
class A {
final int a;
A(int a) {
this.a = a; // Am I fully constructed?
leak();
}
void leak() { }
}
This looks harmless, right? Well, now try:
class B extends A {
String b;
B(int a, String b) { super(a); this.b = b; }
@Override void leak() { System.out.println(b.length()); }
}
The leak() call will be made when the *A* state is fully initialized, but the B state has not yet been initialized. B innocently overrides leak() and is surprised to see a not-fully-initialized B. Whoops.
Moral: the “end” of a constructor is not the end of a constructor.
Moreover, even if you assume A was final, you still lose. Because the final field initialization safety semantics offered by the JMM are dependent on the constructor not leaking `this`. So if you leak `this`, your final field guarantees are voided, and another thread could see the wrong value for `a`. Whoops.
Moral: this stuff is harder than it looks. “Clever” arguments like “but the object must be fully initialized at this point” are stepping out onto some thin ice that requires very, very careful reasoning.
More information about the amber-dev
mailing list