I started working on a technical debt story at my job. I am working on the back-end for a banking application for one of the biggest banks in Romania. This story involved doing some refactoring that will make the application more maintainable and easier to understands. The refactoring, however, managed to baffle me for almost three hours and only after involving the team we managed to solve an error I did not even knew it was possible: NullPointerException
when using the value from an Enum.
To understand things, let’s first look a bit on a simplified version of the architecture. Can’t provide a lot of details, but we use values from an Enum to encode certain information. For example, you can use an Enum
value to specify the role of a user in a system and even have the value assigned to a field inside an Entity
so that you store it in the database. One such Enum
got quite big and complex and, most importantly, was storing two different sub-categories of values. It makes sense in our system, even if it may sound strange here.
As part of the refactoring that I was doing I wanted to split this Enum
into two, one for each sub-category. Because the values are still connected and, most importantly, are mapped to a single column in the database, I had to get creative. The solution was to have an interface and have the two new Enum
classes implement it. This way we have everything backwards compatible.
For simplicity, let’s assume we have a scenario like this:
public interface MyEnumInterface {
}
public enum EnumOne implements MyEnumInterface {
VALUE1,
VALUE2,
VALUE3;
}
public enum EnumTwo implements MyEnumInterface {
VALUE1,
VALUE2,
VALUE3;
}
Now let’s add an option for each value that dictates if entities that have the value are excluded or not from certain processing:
public enum EnumOne implements MyEnumInterface {
VALUE1(true),
VALUE2,
VALUE3(true);
private final boolean excluded;
EnumOne() {
this.excluded = false;
}
EnumOne(boolean excluded) {
this.excluded = excluded;
}
}
public enum EnumTwo implements MyEnumInterface {
VALUE1,
VALUE2(true),
VALUE3;
private final boolean excluded;
EnumTwo() {
this.excluded = false;
}
EnumTwo(boolean excluded) {
this.excluded = excluded;
}
}
Naturally, we enhance the MyEnumInterface
interface to provide a isExcldued
method and have both enums override it. We also want to have a Set
containing the values from both enums that are excluded since we need it somewhere in our business logic. The easiest way is to add this set directly in our interface and have it computed at runtime:
public interface MyEnumInterface {
Set EXCLUDED = Stream.concat(Stream.of(EnumOne.values()), Stream.of(EnumTwo.values()))
.filter(MyEnumInterface::isExcluded)
.collect(Collectors.toSet());
default boolean isExcluded() {
return false;
}
}
NullPointerException when accesing an enum’s value
Do you see any problems with this code? I did not and the IDE did not. The compilation also was successful and no problems were encountered. Until I actually needed to use one of the values in the code. It was only then when I was greeted with a really nice NPE: java.lang.NullPointerException: Cannot invoke "[LEnumOne;.clone()" because "EnumOne.$VALUES" is null
. This was on the initialization of the set.

Going into debug mode and placing a breakpoint where the Set was constructed was even more confusing. Download the code and take a look See for yourself the strangeness of these errors.


How can EnumOne.values()
throw a NullPointerException, but EnumTwo.values()
work just fine? Here are three main()
method. Can you guess which one works and which one throws an exception?
public static void main1(String[] args) {
System.out.println(MyEnumInterface.EXCLUDED.size());
// Prints: 3
}
public static void main2(String[] args) {
System.out.println(EnumOne.VALUE2);
System.out.println(MyEnumInterface.EXCLUDED.size());
// throws exception
}
public static void main3(String[] args) {
System.out.println(MyEnumInterface.EXCLUDED.size());
System.out.println(EnumOne.VALUE2);
// Prints: 3\nVALUE2
}
The fix
I will disappoint you now. I don’t fully understand why this is happening (please leave a comment or contact me if you do), but from what I understand there is a confusion with the initialization flow in this case. The JVM first tries to instantiate the Set, and not the EnumOne
class and it’s values. Because EnumOne
implements MyEnumInterface
, the JVM considers that the interface must be fully initialized prior to initializing the enum, however, this fails since it does not have the Enum values yet.
And I would consider this explanation the end, but I still don’t know why, when in debug mode, I can read EnumTwo
s values, but not EnumOne
s and why doing anything with the set prior to reading one of the values from the enum works. Furthermore, if I try to read a value from EnumTwo
in my main()
, the roles switch and EnumTwo
fails to instatiate while EnumOne
works just fine. So, the one for which I am trying to read a value first is the one that fails to be instantiated. If you have a better understanding of how this works, do leave a message.
On the bright side, I have a solution. Move the Set
to another class. If we initialize the set outside the interface, the problem goes away. Moving the Set
initialization somewhere else, somewhere that has no relation with either enums, means that there is nothing stopping the interface to be properly created prior to having the enums.
So, as a conclusion, never use a Java enum inside the interface it is implementing. You will get strange errors that may not be caught on time. These errors will also be hard to debug and are not caught until runtime, maybe even months after the initial change, when another change triggers the exact scenario described here.
Video
Here is a video about this article with more examples and the problem displayed.