The Java Constants Interface Anti-Pattern
The most common way to define a constant is in a class and using public static final . One can then use the constant in another class using ClassName.CONSTANT_NAME . Constants are usually defined in upper cases as a rule, atleast in Java.
So if I were to define a constant for the value of Pi(π), it would be something like:
public final class Constants public static final double PI = 3.14; >
This can then be used as Constants.PI whenever we want to reference the value of Pi.
Another way one can define constants is by the use of interfaces.
public interface Constants double PI = 3.14; >
However, this is not recommended by most sources on the internet. Why? Because it is an anti-pattern.
But is it really an Anti-pattern?#
Let’s examine the difference by using both the methods.
package constants; public final class MathConstantsClass public static final double PI = 3.14; >
package constants; public interface MathConstantsInterface double PI = 3.14; >
Let us define another interface which will help us test both the above methods.
package operations; public interface CircleArea double calculate(double radius); >
The above interface would help us define a contract to calculate the area of a circle. As we know, the area of a circle is dependent only on its radius, and thus is reflected in the above interface.
The following class provides the implementation of calculating the area of a circle.
import constants.MathConstantsClass; import operations.CircleArea; public class MathConstantsClassImplementation implements CircleArea public double calculate(double radius) return MathConstantsClass.PI * radius * radius; > >
To test the the above code, let us write a Test class using JUnit.
import operations.CircleArea; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class MathConstantsClassImplementationTest @Test void calculate() CircleArea area = new MathConstantsClassImplementation(); double circleArea = area.calculate(1.0); assertEquals(3.14, circleArea); > >
If you run the above piece of test code, the test would pass.
For testing how we can use the constants with Interface, let’s write another class called MathConstantsInterfaceImplementation .
import constants.MathConstantsInterface; import operations.CircleArea; public class MathConstantsInterfaceImplementation implements MathConstantsInterface, CircleArea public double calculate(double radius) return PI * radius * radius; > >
Similarly a test for the above class is as follows:
import operations.CircleArea; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class MathConstantsInterfaceImplementationTest @Test void calculate() CircleArea area = new MathConstantsInterfaceImplementation(); double circleArea = area.calculate(1.0); assertEquals(3.14, circleArea); > >
The above Test would pass. However, the argument against the implementation is that it is not a good practice as there could be field shadowing, and that will override the original value of the constant within the class.
It can be better understood with the following example:
import constants.MathConstantsInterface; import operations.CircleArea; public class MathConstantsWithInterfaceImplementationAndConstantShadowing implements MathConstantsInterface, CircleArea private static final double PI = 200; public double calculate(double radius) return PI * radius * radius; > >
If, by chance, someone overrode the value of PI inside the class, it would lead to an incorrect output. It can be easily verified by the following test.
import operations.CircleArea; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class MathConstantsWithInterfaceImplementationAndConstantShadowingTest @Test void calculate() CircleArea area = new MathConstantsWithInterfaceImplementationAndConstantShadowing(); double circleArea = area.calculate(1.0); assertEquals(3.14, circleArea); > >
The above test fails. The answer returned by calculate() is 200.0 instead of the expected 3.14 . Another argument is, using the interface would pollute the namespace and also lead to the value propagated across the subclasses.
The above arguments are valid, and hold true.
However, what no one mentions is that you can still directly use the constants from the interface without implementing the interface. Just like the first example where we use MathConstantsClass.PI , we can also use MathConstantsInterface.PI without affecting the namespace and inheritance and shadowing issues.
This can also be easily verified:
import constants.MathConstantsInterface; import operations.CircleArea; public class MathConstantsInterfaceWithoutImplementation implements CircleArea public double calculate(double radius) return MathConstantsInterface.PI * radius * radius; > >
import operations.CircleArea; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class MathConstantsInterfaceWithoutImplementationTest @Test void calculate() CircleArea area = new MathConstantsInterfaceWithoutImplementation(); double circleArea = area.calculate(1.0); assertEquals(3.14, circleArea); > >
It would make no difference to our way of implementation. Even the number of imports remain same. Moreover, you do not need additional boilerplate of public static final as members in an interface are public static final by default.
public static final double PI = 3.14;
What would you prefer? Cleaner code, anyone?
I have seen most constants almost grouped together if they are used throughout the application. You could also suggest that interface should only be used for contracts, and in most cases they are. However, keeping an interface for solely storing constants doesn’t seem to be wrong to me either!
Unless, of course, some developer tries to implement a class which solely contains constants – which would beget the question – WHY?
Declaring Constants in Java
Constant in programming is a variable that never changes.
Today I’ll tell you about declaring constants in Java.
Java doesn’t have a special keyword to define a constant.
const is reserved keyword (you can’t use it as a name of a variable for example), but it’s unused.
So to declare a constant in Java you have to add static final modifiers to a class field.
public static final String BASE_PATH = "/api";
You should follow Java constant naming convention – all constant variables should be in upper case, words should be separated by the underscore.
Declaring Constants Class In Java
Sometimes programmers are defining constants in a separate class in Java
First of all, it’s a really bad idea to create a single class for all constants in your project.
Constants should be related to each other.
For example, I have a UserController, that contains endpoints to work with users.
Each endpoint has a binding to specific URL.
So I can create a class with Java string constants that contains user controller bindings.
package com.explainjava; public final class UserBinding < private UserBinding() < >public static final String BASE_PATH = "/api/users"; public static final String FIND_ONE = BASE_PATH + "/"; public static final String FIND_PREFERENCES = BASE_PATH + "//preferences"; >
The best practice of defining constants class in Java is:
- Add a final attribute to class to restrict inheritance.
- Add a private no-args constructor to forbid new instance creation.
Defining Constants in Java Interface
All fields in the interface are constants.
By default, each variable in the interface is public static final and you can’t change it.
I’ll change our UserBinding to an interface.
package com.explainjava; public interface UserBindings < String BASE_PATH = "/api/users"; String FIND_ONE = BASE_PATH + "/"; String FIND_PREFERENCES = BASE_PATH + "//preferences"; >
But defining constants in the interface is a bad practice.
It’s called Constant Interface Antipattern.
Joshua Bloch in his book “Effective Java” said:
The constant interface pattern is a poor use of interfaces. That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class’s exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface.
There are several constant interfaces in the java platform libraries, such as java.io.ObjectStreamConstants . These interfaces should be regarded as anomalies and should not be emulated.
So my recommendation is to use class for constants.
Tips & Tricks
If you’re using Intellij IDEA to write a code I have 3 live templates for you:
psf + TAB generates public static final .
psfs + TAB generates public static final String .
psfi + TAB generates public static final int .
Learn your favorite IDE shortcuts and live templates to increase your productivity.