Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call Enum individual methods?

Tags:

java

enums

I have an enum that might look like the one below. My goal is to have an enum with some common methods (I enforced this by adding an abstract method) and some "enum value individual" methods.

The following code compiles:

public enum MyEnum {

    VALUE {
        @Override
        public void syso() {
            System.out.println("VALUE syso");
        }
    },

    SPECIAL_VALUE {
        @Override
        public void syso() {
            System.out.println("SPECIAL_VALUE syso");
        }

        public void sayHello() {
            System.out.println("Hello");
        }
    };

    public abstract void syso();

    public static void main(String... args) {
        MyEnum.VALUE.syso();
        MyEnum.SPECIAL_VALUE.syso();
    }

}

Running this results in the following being printed:

VALUE syso
SPECIAL_VALUE syso

However trying to call sayHello(), which I defined in SPECIAL_VALUE, does not work.

Adding the following to the main method, does not compile anymore:

public static void main(String... args) {
    MyEnum.SPECIAL_VALUE.sayHello(); // does not work
}

Why is that? It seems perfectly fine to me, but the method cannot be found. Is there any way to invoke this method? Maybe via reflection?

I would like to avoid making this method abstract as well, because it does not make sense for any other enum values. I also cannot extend this enum and add this special method, while inheriting the common ones. I would also like to avoid adding some kind of singleton class to "simulate" this.

Is it anyhow possible to run this? If not, what would be my best alternative?

like image 592
noone Avatar asked Dec 25 '22 02:12

noone


1 Answers

Why is that?

The reason is given in the JLS:

8.9.1. Enum Constants

...

Instance methods declared in enum class bodies may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type (§8.4.8).

Is there any way to invoke this method? Maybe via reflection?

Given the above constraint, reflection is the only alternative if you do not want to expose the method in the enclosing enum class. Each enum constant is created as an inner class, like MyEnum$1 and MyEnum$2 in your example. Thus, you can retrieve the Class through the constant's getClass() method and then call your method through reflection (exception handling omitted):

...
Class c = MyEnum.SPECIAL_VALUE.getClass();
Method m = c.getMethod("sayHello");
m.invoke(MyEnum.SPECIAL_VALUE);
...

I would most likely try to avoid reflection and expose the method in the enum class, and let it throw an UnsupportedOperationException:

...
public void sayHello() {
    throw new UnsupportedOperationException();
}
...

This at least catches unintended calls during runtime - it still does not allow the compiler to catch them during compile time, but neither does the reflection approach.

like image 126
Andreas Fester Avatar answered Dec 27 '22 16:12

Andreas Fester