1. Overview
Utility classes contain only static members that we group together around a specific topic. Thus, the classes themselves are stateless, while their members contain code meant to be reused across several layers.
In this tutorial, we’ll explain why static code analyzers report that utility classes shouldn’t have public constructors. We’ll look at solving that issue by implementing a private constructor. In addition, we’ll explore which Lombok annotations can help us generate one. We’ll also show how to disable these warnings.
Finally, we’ll evaluate some alternative approaches towards implementing utility classes in Java.
2. Utility Classes
Unlike classes that define objects, utility classes don’t save any data or state. They only contain behavior. Utilities contain only static members. All of their methods are static, while data is passed only as method arguments.
2.1. Why Utility Classes?
In object-oriented programming, we’re looking to model our problem domain and group together families of similar functionality.
We may also choose to write pure functions to model common behavior across our codebase, especially when using functional programming. Unlike object methods, these pure functions are not related to an instance of any object. However, they do need a home. Java doesn’t have a specific type set aside for housing a set of functions, so we often create a utility class.
Great examples of popular utility classes in Java are Arrays and Collections from java.util, as well as StringUtils form org.apache.commons.lang3.
2.2. Implementation in Java
Java doesn’t provide a special keyword or a way for creating utility classes. Thus, we usually create a utility class as a plain Java class, but with only static members:
public final class StringUtils {
public static boolean isEmpty(String source) {
return source == null || source.length() == 0;
}
public static String wrap(String source, String wrapWith) {
return isEmpty(source) ? source : wrapWith + source + wrapWith;
}
}
In our example, we marked the utility class as public and final. Utilities are usually made public as they are intended to be reused across several layers.
The final keyword prevents subclassing. Since utility classes are not designed for inheritance, we shouldn’t subclass them.
2.3. Public Constructor Warning
Let’s try to analyze our example utility class using SonarQube, a popular static code analysis tool. We can run a SonarQube analysis on a Java project using the build tool plugin, in this case, Maven:
mvn clean verify sonar:sonar -Dsonar.host.url=http://localhost:9000 -Dsonar.login=XYXYXYXY
The static code analysis results in a major code smell. SonarQube warns us to hide the implicit public constructor in our utility class:
Though we didn’t add a constructor to our utility class, Java implicitly added a default public one. Thus, enabling API users to create an instance of it:
StringUtils utils = new StringUtils();
This is a misuse of our utility classes, as it was not designed to be instantiated. Therefore, the SonarQube rule advises us to add a private constructor in order to hide the default public one.
3. Adding a Private Constructor
Let’s now solve the reported code smell by adding a private constructor in our utility class.
3.1. Default Private Constructor
Let’s add a private constructor with no arguments to our utility class. We will never really use this private constructor. Thus, it is a good practice to throw an exception in case it is called:
public final class StringUtils {
private StringUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
// public static methods
}
We should note that the private constructor also cannot be tested. Thus, this approach will result in one line of uncovered code in our code-coverage measurements.
3.2. Using Lombok NoArgsConstructor
We can make use of the NoArgsConstructor Lombok annotation to auto-generate the private constructor:
@NoArgsConstructor(access= AccessLevel.PRIVATE)
public final class StringUtils {
// public static methods
}
This way, we can avoid manually adding an additional line of uncovered code.
3.3. Using Lombok UtilityClass
We can also use the UtilityClass Lombok annotation that marks an entire class as a utility:
@UtilityClass
public class StringUtils {
// public static methods
}
In this case, Lombok will automatically:
- generate a private constructor that throws an exception
- flag as error any explicit constructors we add
- mark the class final
We should note that, at this time, the UtilityClass annotation is still an experimental feature.
4. Disabling the Warning
If we decide not to follow the recommended solution, we also have an option to disable the public constructor warning.
4.1. Suppressing Warning
Let’s make use of Java’s SuppressWarnings annotation in order to disable the warning on a single class level:
@SuppressWarnings("java:S1118")
public final class StringUtils {
// public static methods
}
We should pass the correct SonarQube rule ID should as a value parameter. We can find it in the SonarQube server UI:
4.2. Deactivating a Rule
In the SonarQube out-of-the-box quality profile, we are not able to deactivate any of the predefined rules. Thus, in order to disable the warning on the complete project level, we first need to create a custom quality profile:
In our custom quality profile, we can search for and deactivate any of the predefined Java rules.
5. Alternative Implementations
Let’s look at some possible alternative ways how to create utilities besides using classes.
5.1. Static Interface Methods
Since Java 8, we can define and implement static methods in interfaces:
public interface StringUtils {
static boolean isEmpty(String source) {
return source == null || source.length() == 0;
}
static String wrap(String source, String wrapWith) {
return isEmpty(source) ? source : wrapWith + source + wrapWith;
}
}
As we cannot instantiate interfaces, we eliminated the utility class instantiation issue. However, we are creating another problem. Since interfaces are designed to be implemented by other classes, an API user could mistakenly implement this interface.
In addition, interfaces cannot contain private constants and static initializers.
5.2. Static Enum Methods
Enums are containers of managed instances. However, we can create a utility as an enum with zero instances containing only static methods:
public enum StringUtils {;
public static boolean isEmpty(String source) {
return source == null || source.length() == 0;
}
public static String wrap(String source, String wrapWith) {
return isEmpty(source) ? source : wrapWith + source + wrapWith;
}
}
As we cannot instantiate enum types, we eliminated the utility class instantiation issue. On the other hand, as the name suggests, enum types are designed for creating actual enumerations, not utility classes.
6. Conclusion
In this article, we explored utility classes and explained why they shouldn’t have public constructors.
In the examples, we covered implementing a private constructor manually and using Lombok annotations. Next, we saw how to suppress and disable the related SonarQube warning. Finally, we looked at two alternative ways to create utilities using interfaces and enums.
As always, the source code is available over on GitHub.