Getting NullPointerException when using an Enum? Here is how to fix it!
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<MyEnumInterface> 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.
I was actually having the exact same issue doing Advent of Code 2023, just it was a final instance field on the enum set to `List.of(this.counterClockwise(), this.clockwise())`, where the respective methods return the previous/next enum value (hence when the enum instance initialized it tried to call those methods, but couldn’t because the next enum value hadn’t been initialized yet). I could see this in the stacktrace from the error:
“`
Exception in thread “main” java.lang.ExceptionInInitializerError
at chiefarug.code.adventofcode.Day16$Direction.clockwise(Day16.java:33) // calling the clockwise method for initializing the field
at chiefarug.code.adventofcode.Day16$Direction.(Day16.java:21) // init of an individual Direction instance which will initialize all the fields
at chiefarug.code.adventofcode.Day16$Direction.(Day16.java:12) // class init of Direction, which will init all the individual Direction instances
at chiefarug.code.adventofcode.Day16.run(Day16.java:189)
at chiefarug.code.adventofcode.AdventOfCode.main(AdventOfCode.java:27)
Caused by: java.lang.NullPointerException: Cannot invoke “[Lchiefarug.code.adventofcode.Day16$Direction;.clone()” because “chiefarug.code.adventofcode.Day16$Direction.$VALUES” is null
at chiefarug.code.adventofcode.Day16$Direction.values(Day16.java:11) // trying to get all of the values of direction, but it cannot because they haven’t all been initted yet, hence the compiler generated array containing them is null
at chiefarug.code.adventofcode.Day16$1.(Day16.java:33)
“`
I fixed it by wrapping the field in a memoized supplier (I was accessing the field via a method anyway so it didn’t increase the code complexity outside that). Code will be on my AoC-2023 github repo when I finish this challenge at `/src/chiefarug/code/adventofcode/Day16.java` around about line 20. (not sure if links are allowed so I’ve included my github as my website, accessible by clicking my name I believe).