Cannot instantiate EnumMap with dynamic class argument for enums with methods

1k views Asked by At

I stumbled across a strange difference in behaviour between enums in Java without any defined methods and those that do: In the latter case, Enum.class and Enum.getClass() actually refer to different compiled classes, i.e. !Enum.class.equals(Enum.getClass()); This causes problems when trying to e.g. instantiate an EnumMap with a class specified only at runtime:

import java.util.EnumMap;

public class EnumMapTest {

    private enum TestEnum {
        FOO;
    }

    private enum TestEnumWithMethod {
        BAR {
            @Override
            protected void doSomething() {
            }
        };

        protected abstract void doSomething();
    }

    public static void main(String[] args) {
        System.out.println(String.format("Testing enum %s...", TestEnum.class));

        final Class<TestEnum> enumStaticClass = TestEnum.class;
        System.out.println(String.format("EnumMap construction using static %s...", enumStaticClass));
        new EnumMap<TestEnum, Object>(enumStaticClass);

        final Class<TestEnum> enumDynamicClass = (Class<TestEnum>) TestEnum.FOO.getClass();
        System.out.println("Are the static and dynamic classes equal? " + enumStaticClass.equals(enumDynamicClass));
        System.out.println(String.format("EnumMap construction using dynamic %s...", enumDynamicClass));
        new EnumMap<TestEnum, Object>(enumDynamicClass);


        System.out.println(String.format("Testing enum %s...", TestEnumWithMethod.class));

        final Class<TestEnumWithMethod> enumWithMethodStaticClass = TestEnumWithMethod.class;
        System.out.println(String.format("EnumMap construction using static %s...", enumWithMethodStaticClass));
        new EnumMap<TestEnumWithMethod, Object>(enumWithMethodStaticClass);

        final Class<TestEnumWithMethod> enumWithMethodDynamicClass = (Class<TestEnumWithMethod>) TestEnumWithMethod.BAR.getClass();
        System.out.println("Are the static and dynamic classes equal? " + enumWithMethodStaticClass.equals(enumWithMethodDynamicClass));
        System.out.println(String.format("EnumMap construction using dynamic %s...", enumWithMethodDynamicClass));
        new EnumMap<TestEnumWithMethod, Object>(enumWithMethodDynamicClass);
    }
}

The corresponding console output is:

Testing enum class EnumMapTest$TestEnum...
EnumMap construction using static class EnumMapTest$TestEnum...
Are the static and dynamic classes equal? true
EnumMap construction using dynamic class EnumMapTest$TestEnum...
Testing enum class EnumMapTest$TestEnumWithMethod...
EnumMap construction using static class EnumMapTest$TestEnumWithMethod...
Are the static and dynamic classes equal? false
EnumMap construction using dynamic class EnumMapTest$TestEnumWithMethod$1...
Exception in thread "main" java.lang.NullPointerException
    at java.util.EnumMap.initialization(EnumMap.java:726)
    at java.util.EnumMap.<init>(EnumMap.java:395)
    at EnumMapTest.main(EnumMapTest.java:46)

Why are there two classes made for the enum with methods? Why does this cause problems during the instantiation of EnumMap? How can I get around this to create an instance without knowing the exact enum type at compile time?

2

There are 2 answers

0
errantlinguist On BEST ANSWER

I found the problem: Enum.getClass() gets the anonymous class of the particular enum instance, e.g. FOO or BAR, while Enum.getDeclaringClass() gets the class of the actual enum. Strange that the latter stills works without methods, though...

5
chrylis -cautiouslyoptimistic- On

The reason for the multiple classes is that the different enum methods are implemented by creating an anonymous inner class (the $1) for each enum value, just as if you'd declared some inline ActionListener. The "workaround" is to use the actual enum class (TestEnumWithMethod), not the class of a value of that enum; if you know enough information to safely use an EnumMap, you know enough information to use the enum's "native" type.