I mentioned in my blog post "Explicit No-Arguments Constructor Versus Default Constructor" that "it is possible that one day javac
will have an available lint warning to point out classes with default constructors." In that post, I referenced JDK-8071961 ("Add javac lint warning when a default constructor is created"), which has now been implemented as of JDK 16 Early Access Build #12. This post introduces that newly available javac -xlint warning.
To see this new javac -Xlint warning in action, one must download at least JDK 16 Early Access Build #12 (19 August 2020) or later.
To demonstrate the new javac -Xlint
warning, we need a class with no explicit constructor so that javac
will generate a "default constructor."
<rant>(By the way, a minor pet peeve of mine is when someone comments an explicit constructor with no arguments with Javadoc text that states "Default constructor." It's not really a default constructor once it's explicitly specified!)</rant>
An example of a class with no explicit constructor is available on GitHub and is shown here:
DefaultConstructor.javapackage dustin.examples.jdk16; import static java.lang.System.out; /** * This class intentionally does NOT specify an explicit constructor * so that a "default constructor" will be generated and trigger the * new JDK 16 warning. */ public class DefaultConstructor { private String name; public String getName() { return name; } public void setName(final String newName) { name = newName; } public static void main(final String[] arguments) { final DefaultConstructor instance = new DefaultConstructor(); instance.setName(arguments.length > 0 ? arguments[0] : ""); out.println("Hello " + instance.getName() + "!"); } }
If we compile the new class with no explicitly specified constructor with javac
provided by the OpenJDK JDK 16 Early Access Build #12 or later, we won't see the new warning demonstrated unless we export the package that class is in and enable -Xlint
warnings. An example of exporting the package is available on GitHub and is shown here:
module dustin.examples { exports dustin.examples.jdk16; }
Compiling with -Xlint
When I run javac -X
with the JDK 16 Early Access Build #12 compiler, I see these Xlint-related options that are now available (emphasis added):
-Xlint Enable recommended warnings -Xlint:(, )* Warnings to enable or disable, separated by comma. Precede a key by - to disable the specified warning. Supported keys are: all Enable all warnings auxiliaryclass Warn about an auxiliary class that is hidden in a source file, and is used from other files. cast Warn about use of unnecessary casts. classfile Warn about issues related to classfile contents. deprecation Warn about use of deprecated items. dep-ann Warn about items marked as deprecated in JavaDoc but not using the @Deprecated annotation. divzero Warn about division by constant integer 0. empty Warn about empty statement after if. exports Warn about issues regarding module exports. fallthrough Warn about falling through from one case of a switch statement to the next. finally Warn about finally clauses that do not terminate normally. missing-explicit-ctor Warn about missing explicit constructors in public classes in exported packages. module Warn about module system related issues. opens Warn about issues regarding module opens. options Warn about issues relating to use of command line options. overloads Warn about issues regarding method overloads. overrides Warn about issues regarding method overrides. path Warn about invalid path elements on the command line. processing Warn about issues regarding annotation processing. rawtypes Warn about use of raw types. removal Warn about use of API that has been marked for removal. requires-automatic Warn about use of automatic modules in the requires clauses. requires-transitive-automatic Warn about automatic modules in requires transitive. serial Warn about Serializable classes that do not provide a serial version ID. Also warn about access to non-public members from a serializable element. static Warn about accessing a static member using an instance. text-blocks Warn about inconsistent white space characters in text block indentation. try Warn about issues relating to use of try blocks (i.e. try-with-resources). unchecked Warn about unchecked operations. varargs Warn about potentially unsafe vararg methods preview Warn about use of preview language features none Disable all warnings
As shown in these usage details, one can use -Xlint
, -Xlint:all
, or -Xlint:missing-explicit-ctor
to see this new warning about default constructors being exposed by classes in publicly exported packages.
Compiling the new class with -Xlint
, -Xlint:all
, or -Xlint:missing-explicit-ctor
demonstrates the new warning about default constructors being used in a formal API:
-Xlint:all
-Xlint and -Xlint:missing-explicit-ctor
As shown in the screenshots, the warning message states (emphasis added by me): "warning: [missing-explicit-ctor] class DefaultConstructor in exported package dustin.examples.jdk16 declares no explicit constructors, thereby exposing a default constructor to clients of module dustin.examples"
The warning message that javac
provides when -Xlint
is appropritely specified describes the issue and specifically calls out the exported package with the offending class and the name of module that exports that package.
Summary of Steps to See Default Constructor Warning
- Download and "install" OpenJDK 16 Early Access Build #12 (or later) from https://jdk.java.net/16/
- Write Java class with no explicitly specified constructor so that
javac
will generate a "default constructor" (example). - Export package with class with no explicit constructor via a
module-info.java
file (example). - Compile class with no explicit constructor with
-Xlint:all
provided to thejavac
compiler.
Not All Classes with No Explicit Constructors Will Be Flagged
Not all Java classes that lack an explicit constructor will lead to this new warning being emitted even when a relevant -Xlint
option is specified. As stated earlier, even the DefaultConstructor
class used in this post's example does not lead to the warning message being generated until it's package is exported in the module-info.java
file. Joe Darcy explains on the OpenJDK compiler-dev mailing list:
In terms of detailed criteria to issue the new warnings, there was the usual tension in warnings between reducing false positives and false negatives. For example, warning for *any* default constructor, even in a throw-away class, would be more annoying than helpful. With some guidance from the JDK code base, criteria in the current patch are a default constructor merits a warning if:
- The class is in a named package and the packaged has an unqualified export from its module AND
- The class is public and, if it is a nested class, all of its lexically enclosing types are public too.
An unqualified export, where the package is available to use to any module and not just named ones, was taken to indicate classes in the package can comprise a "formal API". It would be simple to change this to an unqualified export, but I wanted to avoid unwanted instances of a new warning. If a public nested class is a non-public enclosing class, the nested class is not directly part of the exported API. These combinations of kinds of exports and nesting are tested in the tests in the DefaultCtor directory.
Why Warn on Use of Default Constructors in a "Formal API" Class?
The previously mentioned Joe Darcy post explains why this warning has been added:
Some background on the design of the warning and broader usage context, while default constructors can be convenient for informal code, they can be a bit troublesome for more formal APIs, such as the public classes of packages in the JDK. One issue is that default constructors do not have javadoc. Another is that a class that semantically should not be instantiated, say it is a solely a holder for static constants and methods, can get instantiated and subclassed. (Subclasssing such a class is an anti-pattern to allow use short names for the static members, which is no longer necessary since static imports as of Java SE 5.0.)
Conclusion
This seemingly small change to add this new warning about "default constructors" in "formal API" classes has required more effort than might initially be assumed. A large number of issues were written to not only introduce the xlint warning, but to clean up numerous classes throughout the JDK that triggered this warning when compiled. Furthermore, naming and logging can often be tricky and the particular warning mssage went through review and iterative changes as well.
1 comment:
Joe Darcy's 25 August 2020 message on the OpenJDK compiler-dev mailing list states, "... there are cases in the JDK where a protected class has a default constructor appearing in the JDK." Based on this Darcy, requests review of changes that "augment the warning to cover both public and protected classes." He adds, "For nested classes, the enclosing types must be either public or protected all the way up for the warning to be issued."
Post a Comment