This is a short blog post about a bug in my code that stumped me for some moments. I try to tell it in a manner where you can follow the story and try to find the solution before I reveal it. You can also just read along and learn something about Java Enums and my coding style.
A code structure that I use sometimes is the Enum type that implements an interface:
public enum BuiltinTopic implements Topic {
administration("Administration"),
userStatistics("User Statistics"),
;
private final String denotation;
private BuiltinTopic(String denotation) {
this.denotation = denotation;
}
@Override
public String denotation() {
return this.denotation;
}
}
The Topic interface is nothing special in this example. It serves as a decoupling layer for the (often large) part of client code that doesn’t need to know about any specifics that stem from the Enum type. It helps with writing tests that aren’t coupled to locked-down types like Enums. It is just some lines of code:
public interface Topic {
String denotation();
}
Right now, everything is fine. The problems start when I discovered that the denotation text is suited for the user interface, but not for the configuration. In order to be used in the configuration section of the application, it must not contain spaces. Ok, so let’s introduce a name concept and derive it from the denotation:
public interface Topic {
String denotation();
default String name() {
return Without.spaces(denotation());
}
}
I’ve chosen a default method in the interface so that all subclasses have the same behaviour. The Without.spaces() method does exactly what the name implies.
The new method works well in tests:
@Test
public void name_contains_no_spaces() {
Topic target = () -> "User Statistics";
assertEquals(
"UserStatistics",
target.name()
);
}
The perplexing thing was that it didn’t work in production. The names that were used to look up the configuration entries didn’t match the expected ones. The capitalization was wrong!
To illustrate the effect, take a look at the following test:
@Test
public void name_contains_no_spaces() {
Topic target = BuiltinTopic.userStatistics;
assertEquals(
"userStatistics",
target.name()
);
}
You can probably spot the difference in the assertion. It is “userStatistics” instead of “UserStatistics”. For a computer, that’s a whole different text. Why does the capitalization of the name change from testing to production?
The answer lies in the initialization of the test’s target variable:
In the first test, I use an ad-hoc subtype of Topic.
In the second test and in production, I use an object of type BuiltinTopic. This object is an instance of an Enum.
In Java, Enum classes and Enum objects are enriched with automatically generated methods. One of these methods equip Enum instances with a name() method that has a default implementation to return the Enum instances’ variable/constant name. Which in my case is “userStatistics”, the same string I expect, minus the correct capitalization of the first character.
If I had named the Enum instance “UserStatistics”, everything would have worked out until somebody changes the name or adds another instance with a slight difference in naming.
If I had named my Enum instance something totally different like “topic2”, it would have been obvious. But in this case, with only the minor deviation, I was compelled to search for problems elsewhere.
The problem is that the auto-generated name() method overwrites my default method, but only in cases of real Enum instances.
So I thought hard about the name of the name() method and decided that I don’t really want a name(), I want an identifier(). And that made the problem go away:
public interface Topic {
String denotation();
default String identifier() {
return Without.spaces(denotation());
}
}
Because the configuration code only refers to the Topic type, it cannot see the name() method anymore and only uses the identifier() that creates the correct strings.
I don’t see any (sane) way to prohibit the Java Enum from automatically overwriting my methods when the signature matches. So it feels natural to sidestep the problem by changing names.
Which shows once more that naming is hard. And soft-restricting certain names like Java Enums do doesn’t lighten the burden for the programmer.






