As i twittered a few days ago, I stumbled over a subtle issue in the core Java library. Not as extreme as here but one to raise ones eyebrows, too.
I just wanted to write a small method to access an enum value by its String
representation or use a default, if the conversion fails:
public static <T extends Enum<T>> T toEnum(String value, T default) {
// Checking values omitted
return (T) Enum.valueOf((Class) defaultValue.getClass(), StringUtils
.upperCase(value))
}
Of course, I wrote a unittest to verify the behaviour and it worked fine. Nevetheless using this class in my application threw an exception for a different (than the one in the testcase) Enum
type, claiming the type handed to the valueOf(…)
method was not an Enum?
What? I’ve handed over an Enum
as default value and you tell me, it is not of an Enum
type. I digged down into the details. Looked like Enums with methods broke the code, Enums without methods worked. Seems like the compiler works with anonymous inner classes when creating byte code. The solution to the problem is using getDeclaringClass()
instead, as the JavaDoc reveals.
Although I understand that Enum
s with methods are implemented with inner classes, I do not understand why these inner classes do not extend Enum<T>
. Futhermore I cannot believe that the behaviour of getClass()
changes depending on whether you use methods in your Enum
or not. Strange thing…
Below you find a small test case to reproduce the issue. Be sure you have JUnit in version 4 or above on the classpath.
public class EnumSample {
private enum Foo {
FOO;
}
private enum Bar {
BAR {
@Override
public void doSomething() {
System.out.println("INVOKED!");
}
};
public abstract void doSomething();
}
@SuppressWarnings("unchecked")
private <T extends Enum<T>> T toEnum(String value, T defaultValue) {
return (T) Enum.valueOf(defaultValue.getClass(), value);
}
@Test
public void useSimpleEnum() {
toEnum("FOO", Foo.FOO);
}
@Test(expected = IllegalArgumentException.class)
public void useComplexEnum() {
toEnum("BAR", Bar.BAR);
}
}