Tuesday, May 21, 2019

Explicit No-Arguments Constructor Versus Default Constructor

Most developers new to Java quickly learn that a "default constructor" is implicitly created (by javac) for their Java classes when they don't specify at least one explicit constructor. Section 8.8.9 of the Java Language Specification succinctly states, "If a class contains no constructor declarations, then a default constructor is implicitly declared." That section further describes characteristics of the implicitly created default constructor including it having no parameters, having no throws clause, and invoking the constructor of its super class that similarly accepts no arguments. A Java developer can choose to explicitly implement a no-arguments constructor that is similar to the default constructor (such as accepting no arguments and having no throws clause). In this post, I look at some reasons a developer might decide to implement an explicit no-arguments constructor rather than relying on the implicit default constructor.

Some Reasons to Explicitly Specify No-Arguments Constructors

Preclude Instantiation of a Class

A common reason for implementing an explicit no-arguments constructor is to preclude the default constructor from being implicitly created with public accessibility. This is an unnecessary step if the class has other explicit constructors (that accept parameters) because the presence of any explicit constructor will prevent the implicit default constructor from being generated. However, if there is no other explicit constructor present (such as in a "utility" class with all static methods), the implicit default constructor can be precluded by implement an explicit no-arguments constructor with private access. Section 8.8.10 of the Java Language Specification describes use of all private explicit constructors to prevent instantiation of a class.

Force Class Instantiation via Builder or Static Initialization Factory

Another reason to explicitly implement a private no-arguments constructor is to force instantiation of an object of that class via static initialization factory methods or builders instead of constructors. The first two items of Effective Java (Third Edition) outline advantages of using static initialization factory methods and builders over direct use of constructors.

Multiple Constructors Required Including No-arguments Constructor

An obvious reason for implementing a no-arguments constructor that might be as common or even more common than the reason discussed above is when a no-arguments constructor is needed, but so are constructors that expect arguments. In this case, because of the presence of other constructors expecting arguments, a no-arguments constructor must be explicitly created because a default constructor is never implicitly created for a class that already has one or more explicit constructors.

Document Object Construction with Javadoc

Another reason for explicitly implementing a no-arguments constructor rather than relying on the implicitly created default constructor is to express Javadoc comments on the constructor. This is the stated justification for JDK-8224174 ("java.lang.Number has a default constructor") that is now part of JDK 13 and is also expressed in currently unresolved JDK-8071961 ("Add javac lint warning when a default constructor is created"). Recently written CSR JDK-8224232 ("java.lang.Number has a default constructor") elaborates on this point: "Default constructors are inappropriate for well-documented APIs."

Preference for Explicit Over Implicit

Some developers generally prefer explicit specification over implicit creation. There are several areas in Java in which a choice can be made between explicit specification or the implicit counterpart. Developers might prefer an explicit no-arguments constructor over an implicit constructor if they value the communicative aspect or presumed greater readability of an explicit constructor.

Replacing Default Constructors with Explicit No-Arguments Constructors in the JDK

There are cases in the JDK in which implicit default constructors have been replaced with explicit no-arguments constructors. These include the following:

  • JDK-8071959 ("java.lang.Object uses implicit default constructor"), which was addressed in JDK 9, replaced java.lang.Object's "default constructor" with an explicit no-arguments constructor. Reading the "Description" of this issue made me smile: "When revising some documentation on java.lang.Object (JDK-8071434), it was noted that the class did *not* have an explicit constructor and instead relied on javac to create an implicit default constructor. How embarrassing!"
  • JDK-8177153 ("LambdaMetafactory has default constructor"), which was addressed in JDK 9, replaced an implicit default constructor with an explicit (and private) no-arguments constructor.
  • JDK-8224174 ("java.lang.Number has a default constructor"), which is planned for JDK 13, will replace java.lang.Number's implicit default constructor with an explicit no-arguments constructor.

Potential javac lint Warning Regarding Default Constructors

It is possible that one day javac will have an available lint warning to point out classes with default constructors. JDK-8071961 ("Add javac lint warning when a default constructor is created"), which is not currently targeted for any specific JDK release, states: "JLS section 8.8.9 documents that if a class does not declare at least one constructor, the compiler will generate a constructor by default. While this policy may be convenient, for formal classes it is a poor programming practice, if for no other reason that the default constructor will have no javadoc. Use of a default constructor may be a reasonable javac lint warning."

Conclusion

Relying on default constructors to be created at compile time is definitely convenient, but there are situations in which it may be preferable to explicitly specify a no-arguments constructor even when explicit specification is not required.

No comments: