What is the best way to implement constants in Java?
One approach that we should really avoid : using interfaces to define constants.
Creating a interface specifically to declare constants is really the worst thing : it defeats the reason why interfaces were designed : defining method(s) contract.
Even if an interface already exists to address a specific need, declaring the constants in them make really not sense as constants should not make part of the API and the contract provided to client classes.
To simplify, we have broadly 4 valid approaches.
With static final String/Integer
field :
- 1) using a class that declares constants inside but not only.
- 1 variant) creating a class dedicated to only declare constants.
With Java 5 enum
:
- 2) declaring the enum in a related purpose class (so as a nested class).
- 2 variant) creating the enum as a standalone class (so defined in its own class file).
TLDR : Which is the best way and where locate the constants ?
In most of cases, the enum way is probably finer than the static final String/Integer
way and personally I think that the static final String/Integer
way should be used only if we have good reasons to not use enums.
And about where we should declare the constant values, the idea is to search whether there is a single existing class that owns a specific and strong functional cohesion with constant values. If we find such a class, we should use it as the constants holder. Otherwise, the constant should be associated to no one particular class.
static final String
/ static final Integer
versus enum
Enums usage is really a way to strongly considered.
Enums have a great advantage over String
or Integer
constant field.
They set a stronger compilation constraint.
If you define a method that takes the enum as parameter, you can only pass a enum value defined in the enum class(or null).
With String and Integer you can substitute them with any values of compatible type and the compilation will be fine even if the value is not a defined constant in the static final String
/ static final Integer
fields.
For example, below two constants defined in a class as static final String
fields :
public class MyClass{
public static final String ONE_CONSTANT = "value";
public static final String ANOTHER_CONSTANT = "other value";
. . .
}
Here a method that expects to have one of these constants as parameter :
public void process(String constantExpected){
...
}
You can invoke it in this way :
process(MyClass.ONE_CONSTANT);
or
process(MyClass.ANOTHER_CONSTANT);
But no compilation constraint prevents you from invoking it in this way :
process("a not defined constant value");
You would have the error only at runtime and only if you do at a time a check on the transmitted value.
With enum, checks are not required as the client could only pass a enum value in a enum parameter.
For example, here two values defined in a enum class (so constant out of the box):
public enum MyEnum {
ONE_CONSTANT("value"), ANOTHER_CONSTANT(" another value");
private String value;
MyEnum(String value) {
this.value = value;
}
...
}
Here a method that expects to have one of these enum values as parameter :
public void process(MyEnum myEnum){
...
}
You can invoke it in this way :
process(MyEnum.ONE_CONSTANT);
or
process(MyEnum.ANOTHER_CONSTANT);
But the compilation will never allow you from invoking it in this way :
process("a not defined constant value");
Where should we declare the constants ?
If your application contains a single existing class that owns a specific and strong functional cohesion with the constant values, the 1) and the 2) appear more intuitive.
Generally, it eases the use of the constants if these are declared in the main class that manipulates them or that has a name very natural to guess that we will find it inside.
For example in the JDK library, the exponential and pi constant values are declared in a class that declare not only constant declarations (java.lang.Math
).
public final class Math {
...
public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;
...
}
The clients using mathematics functions rely often on the Math
class.
So, they may find constants easily enough and can also remember where E
and PI
are defined in a very natural way.
If your application doesn't contain an existing class that has a very specific and strong functional cohesion with the constant values, the 1 variant) and the 2 variant) ways appear more intuitive.
Generally, it doesn't ease the use of the constants if these are declared in one class that manipulates them while we have also 3 or 4 other classes that manipulate them as much as and no one of these classes seems be more natural than others to host constant values.
Here, defining a custom class to hold only constant values makes sense.
For example in the JDK library, the java.util.concurrent.TimeUnit
enum is not declared in a specific class as there is not really one and only one JDK specific class that appear as the most intuitive to hold it :
public enum TimeUnit {
NANOSECONDS {
.....
},
MICROSECONDS {
.....
},
MILLISECONDS {
.....
},
SECONDS {
.....
},
.....
}
Many classes declared in java.util.concurrent
use them :
BlockingQueue
, ArrayBlockingQueue<E>
, CompletableFuture
, ExecutorService
, ... and really no one of them seems more appropriate to hold the enum.