118

I would like to declare an enum Direction, that has a method that returns the opposite direction (the following is not syntactically correct, i.e, enums cannot be instantiated, but it illustrates my point). Is this possible in Java?

Here is the code:

public enum Direction {

     NORTH(1),
     SOUTH(-1),
     EAST(-2),
     WEST(2);

     Direction(int code){
          this.code=code;
     }
     protected int code;
     public int getCode() {
           return this.code;
     }
     static Direction getOppositeDirection(Direction d){
           return new Direction(d.getCode() * -1);
     }
}
Pshemo
  • 122,468
  • 25
  • 185
  • 269
Skyler
  • 2,834
  • 5
  • 22
  • 34

6 Answers6

219

For those lured here by title: yes, you can define your own methods in your enum.

If you are wondering how to invoke your own non-static enum methods, you do it same way as with any other non-static method - you invoke it on instance of type which defines/inherits such method.

In case of enums such instances are simply ENUM_VALUEs themselves.

So all you need is YourEnum.YOUR_ENUM_VALUE.yourMethod(arguments).


Now lets go back to the problem from question. One of solutions could be

public enum Direction {
    
    NORTH, SOUTH, EAST, WEST;
    
    private Direction opposite;
    
    static {
        NORTH.opposite = SOUTH;
        SOUTH.opposite = NORTH;
        EAST.opposite = WEST;
        WEST.opposite = EAST;
    }
    
    public Direction getOppositeDirection() {
        return opposite;
    }
    
}

Now Direction.NORTH.getOppositeDirection() will return Direction.SOUTH.


Here is little more "hacky" way to illustrate @jedwards comment but it doesn't feel as flexible as first approach since adding more fields or changing their order will break our code.

public enum Direction {
    NORTH, EAST, SOUTH, WEST;
    
    // cached values to avoid recreating such array each time method is called
    private static final Direction[] VALUES = values();

    public Direction getOppositeDirection() {
        return VALUES[(ordinal() + 2) % 4]; 
    }
}
Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • 3
    I was about to whip up an `.values()[ordinal()]` hack but this approach is much more robust – jedwards Sep 18 '13 at 23:04
  • how do you use it, though? and what is this technique called? – Thufir Jul 07 '14 at 06:46
  • 1
    @Thufir "*how do you use it*" if you are asking about method, then like any other method - you invoke it on instance of class with this method. Instances of `Direction` enum class are `NORTH`, `EAST`, `SOUTH`, `WEST` so you can just use `NORTH.getOppositeDirection()` and it will return `SOUTH`. "*what is this technique called?*" if you are asking about `static{...}` then it is [static initialization block](http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html), it is code invoked when class is loaded first time (it is part of same process which initialize static fields). – Pshemo Jul 07 '14 at 13:47
  • @Pshemo, I wonder how the Spring version of above code will be, if the values that are getting set in the static block needs to be injected from say properties file. – Vikas Prasad May 08 '17 at 15:53
167

For a small enum like this, I find the most readable solution to be:

public enum Direction {

    NORTH {
        @Override
        public Direction getOppositeDirection() {
            return SOUTH;
        }
    }, 
    SOUTH {
        @Override
        public Direction getOppositeDirection() {
            return NORTH;
        }
    },
    EAST {
        @Override
        public Direction getOppositeDirection() {
            return WEST;
        }
    },
    WEST {
        @Override
        public Direction getOppositeDirection() {
            return EAST;
        }
    };


    public abstract Direction getOppositeDirection();

}
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • 8
    Great idea! This is also good when you generally want each enum value to have a specific behavior. – OferBr Jun 09 '14 at 11:09
29

This works:

public enum Direction {
    NORTH, SOUTH, EAST, WEST;

    public Direction oppose() {
        switch(this) {
            case NORTH: return SOUTH;
            case SOUTH: return NORTH;
            case EAST:  return WEST;
            case WEST:  return EAST;
        }
        throw new RuntimeException("Case not implemented");
    }
}
Paul Wintz
  • 2,542
  • 1
  • 19
  • 33
Pik'
  • 6,819
  • 1
  • 28
  • 24
  • 9
    Rather than returning null a default clause that throws a suitable RuntimeException might be better to indicate that there was a programmer error in not defining an opposite for a newly added direction. – Timothy055 May 29 '15 at 20:23
  • 2
    This requires the caller to handle null. It also requires the maintainer to make sure they add a case every time a new Direction type is added. See Amir Afghani's answer about using a abstract method that can be overridden by each enum value, that way you never risk missing one, and you don't have to worry about handling null. – Michael Peterson Jan 13 '16 at 17:46
16

Create an abstract method, and have each of your enumeration values override it. Since you know the opposite while you're creating it, there's no need to dynamically generate or create it.

It doesn't read nicely though; perhaps a switch would be more manageable?

public enum Direction {
    NORTH(1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.SOUTH;
        }
    },
    SOUTH(-1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.NORTH;
        }
    },
    EAST(-2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.WEST;
        }
    },
    WEST(2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.EAST;
        }
    };

    Direction(int code){
        this.code=code;
    }
    protected int code;

    public int getCode() {
        return this.code;
    }

    public abstract Direction getOppositeDirection();
}
Makoto
  • 104,088
  • 27
  • 192
  • 230
4

Yes we do it all the time. You return a static instance rather than a new Object

 static Direction getOppositeDirection(Direction d){
       Direction result = null;
       if (d != null){
           int newCode = -d.getCode();
           for (Direction direction : Direction.values()){
               if (d.getCode() == newCode){
                   result = direction;
               }
           }
       }
       return result;
 }
BevynQ
  • 8,089
  • 4
  • 25
  • 37
0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    public Direction getOppositeDirection(){
        return Direction.values()[(this.ordinal() + 2) % 4];
    }
}

Enums have a static values method that returns an array containing all of the values of the enum in the order they are declared. source

since NORTH gets 1, EAST gets 2, SOUTH gets 3, WEST gets 4; you can create a simple equation to get the opposite one:

(value + 2) % 4

Pregunton
  • 77
  • 7
  • 2
    _why_ is this the answer? How do you expect this to help future readers, or anyone for that matter, learn without any explanation? – GrumpyCrouton Jun 26 '17 at 20:23
  • While this code may answer the question, providing additional [context](https://meta.stackexchange.com/q/114762) regarding how and/or why it solves the problem would improve the answer's long-term value. Remember that you are answering the question for readers in the future, not just the person asking now! Please edit your answer to add an explanation, and give an indication of what limitations and assumptions apply. It also doesn't hurt to mention why this answer is more appropriate than others. – ItamarG3 Jun 27 '17 at 06:28
  • is it hard to read code without comments? or do you need a javadoc exclusive for 7 lines of code? – Pregunton Jun 27 '17 at 21:27
  • 1
    This solution is brittle as it depends on the ordering of the enum values. If you were to change the order to be alphabetic, your clever equation would no longer provide the correct opposite. – Josh J Dec 13 '18 at 02:07