javaenums

Why do we need abstract methods in Enums


I've been studying Java for some time now to better understand what goes on inside some of the code parts.

While studying Enum, which I am used to using exclusively for listing various fixed values (such as days of the week), I came across something that I didn't quite understand - the implementation of abstract methods within Enum.

My question is as follows: first, is this mechanism even necessary today? It looks like code smell, although I can't explain why.

Second, how often is Enum used in this way? I understand that there can always be exceptional cases, but it seems to make the code quite difficult to read.

Here is an example:

public enum Operation {
    ADD {
        @Override
        public int apply(int x, int y) {
            return x + y;
        }
    },
    SUBTRACT {
        @Override
        public int apply(int x, int y) {
            return x  -  y;
        }
    };
    public abstract int apply(int x, int y);
}

I tried to find a practical use for it and found only one thing - development in Java for Android, and there I generally understand the meaning of the mechanism, but for web development it looks like an outdated mechanism that only spoils the code.


Solution

  • An abstract method is a guarantee that all implementations will have that method. The compiler will enforce this. If you get an Operation from somewhere, how would you know you could call apply on it?

    If you comment out that function definition, the code doesn't compile anymore with this simple main:

    public static void main(String args[]) {
        Operation op = ADD;
        System.out.println(op.apply(1, 2));
    }
    

    This fails with:

    Operation.java:19: error: cannot find symbol
            System.out.println(op.apply(1, 2));
                                 ^
      symbol:   method apply(int,int)
      location: variable op of type Operation
    

    As you can see, the compiler knows that op is an Operation. It does not know which operation it is. If you were to add public int foo() { return 0; } to ADD, the compiler wouldn't see it:

    Operation.java:22: error: cannot find symbol
            System.out.println(op.foo());
                                 ^
      symbol:   method foo()
      location: variable op of type Operation
    1 error
    

    Looking at the language specification you see that it talks about "enum classes". The spec also says:

    The [...] class body of an enum constant implicitly declares an anonymous class [...] that [...] is a direct subclass of the immediately enclosing enum class

    With that you're back at regular class hierarchies: deriving classes (or here enum values) must implement abstract methods of their base classes, or be declared abstract themselves. Then you just need to know that you have an Operation (base class), but not which child class it is. You can call its abstract methods.

    As for whether this is outdated: No. "Example 8.9.3-3. Enum Constants with Class Bodies" in the Language Specification uses the same examples your code. If you want to call methods on enums and have each enum value behave differently, this is the only way to go. If apply was not abstract, you'd have to define a body for it in Operation and that would always be called, independently of the actual operation, unless you override it. Which you may forget.

    As to "why have functions on enums" and "where should this code live" --- I think it fits perfectly on the enum values. Each operation knows how to apply itself to its operands. The alternative would be some big switch statement that tries to check what operation it has and then call some code to do the operation. This is a code smell: each time you add a new enum value, you must remember to update the switch. The suggested refactoring here is replace conditional with polymorphism, which gets you to your initial enum.