THe OpenJDK 21 beta 15 early access build (released 23 March 2023) adds an -Xlint warning to the Java compiler to notify Java developers when a class's constructor calls an overridable method. Specifically, changes for JDK-8015831 ("Add lint check for calling overridable methods from a constructor") and JDK-6557145 ("Warn about calling abstract methods in constructors") are avalable in OpenJDK 21 beta 15. CSR JDK-8299995 ("Add lint check for calling overridable methods from a constructor") provides background details and justificatiion for these new warnings.
Two example classes are needed to demonstrate the new warnings with a parent class whose constructor calls overridable methods implemented by the extending class.
ParentWithAbstractMethod
: Parent Class with abstract
Method Called by Constructor
package dustin.examples.constructorcalls; public abstract class ParentWithAbstractMethod { /** * Constructor to be called by extending classes. */ protected ParentWithAbstractMethod() { initializeCustomLogic(); } /** * Initialize instance's custom logic. * * Because this {@code abstract} method is called by the constructor, * it will trigger the "this-escape" {@code -Xlint} warning. */ protected abstract void initializeCustomLogic(); }
ConstructorCallsDemonstration
: The Child/Extending Class
package dustin.examples.constructorcalls; /** * Demonstrate warnings introduced with JDK 21 Early Access Update b15. */ public class ConstructorCallsDemonstration extends ParentWithAbstractMethod { public ConstructorCallsDemonstration() { overridableInitializer(); privateInitializer(); finalInitializer(); initializeCustomLogic(); } /** * A child class CAN override this method called by constructor * and will trigger "this-escape" {@code -Xlint} warning. */ protected void overridableInitializer() { } /** * A child class cannot override this method called by constructor * and will NOT trigger "this-escape" {@code -Xlint} warning. */ private void privateInitializer() { } /** * A child class cannot override this method called by constructor * and will NOT trigger "this-escape" {@code -Xlint} warning. */ protected final void finalInitializer() { } @Override protected void initializeCustomLogic() { } }
When the above code is compiled with javac -Xlint
, the new warnings are seen:
The command "javac -Xlint -sourcepath src -d classes src\dustin\examples\constructorcalls\*.java
" generates this output:
src\dustin\examples\constructorcalls\ConstructorCallsDemonstration.java:10: warning: [this-escape] possible 'this' escape before subclass is fully initialized overridableInitializer(); ^ src\dustin\examples\constructorcalls\ParentWithAbstractMethod.java:10: warning: [this-escape] possible 'this' escape before subclass is fully initialized initializeCustomLogic(); ^ 2 warnings
Executing the two code snippets shown above results in several observations:
- An
abstract
method called from a constructor will lead to the new-Xlint this-escape
warning. - A concrete method that is overridable (not
private
orfinal
) and called from a constructor will lead to the new-Xlint this-escape
warning. - A
final
method will not cause the-Xlint this-escape
warning to appear because sub-classes cannot override afinal
method. - A
private
method will not cause the-Xlint this-escape
warning to appear because sub-classes cannot override (or even "see") aprivate
method.
The associated CSR points out that just the new -Xlint this-escape
warning can be enabled when running javac
by specifying -Xlint:this-escape
and this warning can be suppressed by applying the annotation @SuppressWarnings("this-escape")
to the code for which the new warning should be suppressed.