Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding abstract method or using one single method in enums?

Tags:

java

enums

Consider the below enums, which is better? Both of them can be used exactly the same way, but what are their advantages over each other?

1. Overriding abstract method:

public enum Direction {
    UP {
        @Override
        public Direction getOppposite() {
            return DOWN;
        }
        @Override
        public Direction getRotateClockwise() {
            return RIGHT;
        }
        @Override
        public Direction getRotateAnticlockwise() {
            return LEFT;
        }
    },
    /* DOWN, LEFT and RIGHT skipped */
    ;
    public abstract Direction getOppposite();
    public abstract Direction getRotateClockwise();
    public abstract Direction getRotateAnticlockwise();
}

2. Using a single method:

public enum Orientation {
    UP, DOWN, LEFT, RIGHT;
    public Orientation getOppposite() {
        switch (this) {
        case UP:
            return DOWN;
        case DOWN:
            return UP;
        case LEFT:
            return RIGHT;
        case RIGHT:
            return LEFT;
        default:
            return null;
        }
    }
    /* getRotateClockwise and getRotateAnticlockwise skipped */
}

Edit: I really hope to see some well reasoned/elaborated answers, with evidences/sources to particular claims. Most existing answers regarding performance isn't really convincing due to the lack of proves.

You can suggest alternatives, but it have to be clear how it's better than the ones stated and/or how the stated ones is worse, and provide evidences when needed.

like image 778
Alvin Wong Avatar asked Feb 13 '13 10:02

Alvin Wong


People also ask

Can you use abstract methods in enum?

You can also have abstract methods inside Enum type. If there's an abstract method, then each enum constant value should implement it. It is very useful when you want a different implementation of the method for each constant value.

Can enum be override?

By default, the enum value is its method name. You can however override it, for example if you want to store enums as integers in a database, instead of using their method name.

Can we override enum in Java?

Enums are exactly final inner classes that extends java. lang. Enum<E> . You cannot extend, override or inherit an enum .

Can we achieve polymorphism using enums?

The enum can implement an interface that allows it to be polymorphic.


2 Answers

Forget about performance in this comparison; it would take a truly massive enum for there to be a meaningful performance difference between the two methodologies.

Let's focus instead on maintainability. Suppose you finish coding your Direction enum and eventually move on to a more prestigious project. Meanwhile, another developer is given ownership of your old code including Direction - let's call him Jimmy.

At some point, requirements dictate that Jimmy add two new directions: FORWARD and BACKWARD. Jimmy is tired and overworked and does not bother to fully research how this would affect existing functionality - he just does it. Let's see what happens now:

1. Overriding abstract method:

Jimmy immediately gets a compiler error (actually he probably would've spotted the method overrides right below the enum constant declarations). In any case, the problem is spotted and fixed at compile time.

2. Using a single method:

Jimmy doesn't get a compiler error, or even an incomplete switch warning from his IDE, since your switch already has a default case. Later, at runtime, a certain piece of code calls FORWARD.getOpposite(), which returns null. This causes unexpected behavior and at best quickly causes a NullPointerException to be thrown.

Let's back up and pretend you added some future-proofing instead:

default:
    throw new UnsupportedOperationException("Unexpected Direction!");

Even then the problem wouldn't be discovered until runtime. Hopefully the project is properly tested!

Now, your Direction example is pretty simple so this scenario might seem exaggerated. In practice though, enums can grow into a maintenance problem as easily as other classes. In a larger, older code base with multiple developers resilience to refactoring is a legitimate concern. Many people talk about optimizing code but they can forget that dev time needs to be optimized too - and that includes coding to prevent mistakes.

Edit: A note under JLS Example §8.9.2-4 seems to agree:

Constant-specific class bodies attach behaviors to the constants. [This] pattern is much safer than using a switch statement in the base type... as the pattern precludes the possibility of forgetting to add a behavior for a new constant (since the enum declaration would cause a compile-time error).

like image 177
Paul Bellora Avatar answered Sep 22 '22 05:09

Paul Bellora


I actually do something different. Your solutions have setbacks: abstract overridden methods introduce quite a lot of overhead, and switch statements are pretty hard to maintain.

I suggest the following pattern (applied to your problem):

public enum Direction {
    UP, RIGHT, DOWN, LEFT;

    static {
      Direction.UP.setValues(DOWN, RIGHT, LEFT);
      Direction.RIGHT.setValues(LEFT, DOWN, UP);
      Direction.DOWN.setValues(UP, LEFT, RIGHT);
      Direction.LEFT.setValues(RIGHT, UP, DOWN);
    }

    private void setValues(Direction opposite, Direction clockwise, Direction anticlockwise){
        this.opposite = opposite;
        this. clockwise= clockwise;
        this. anticlockwise= anticlockwise;
    }

    Direction opposite;
    Direction clockwise;
    Direction anticlockwise;

    public final Direction getOppposite() { return opposite; }
    public final Direction getRotateClockwise() { return clockwise; }
    public final Direction getRotateAnticlockwise() { return anticlockwise; }
}

With such design you:

  • never forget to set a direction, because it is enforced by the constructor (in case case you could)

  • have little method call overhead, because the method is final, not virtual

  • clean and short code

  • you can however forget to set one direction's values

like image 35
Dariusz Avatar answered Sep 18 '22 05:09

Dariusz