Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum of directions with opposites [duplicate]

Tags:

java

enums

I'm currently working on a Dungeon game project which is composed of multiples rooms connected to each other.

Depending on the room you're in you can go to rooms next to yours by picking directions (North, South, East, West).

I've represented the directions with an Enum class and I'm looking for a way to implements that North is the opposite of South, East for West (and so on ...) for the sake of creating the layout of all the rooms in the final form of the game.

Here is my Enum class :

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

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

}

The problem is that currently the method is not very safe, any change in the order of declarations in the direction will break the method, any suggestions on how to do this in a better way?

like image 279
Rémy.W Avatar asked Oct 07 '18 10:10

Rémy.W


3 Answers

You could make the method abstract, and implement it for each constant.

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

    public abstract Direction getOppositeDirection();
}

It's a bit wordy, though.

The next method is shorter but more difficult to read.

enum Direction {
    NORTH, EAST, SOUTH, WEST;

    static {
        (SOUTH.oppositeDirection = NORTH).oppositeDirection = SOUTH;
        (WEST.oppositeDirection = EAST).oppositeDirection = WEST;
    }

    private Direction oppositeDirection;

    public Direction getOppositeDirection() {
        return oppositeDirection;
    }
}
like image 78
Andrew Tobilko Avatar answered Sep 23 '22 08:09

Andrew Tobilko


The concise answer is:

public Direction getOppositeDirection() {
    return
        this==NORTH ? SOUTH :
        this==EAST  ? WEST :
        this==SOUTH ? NORTH :
                      EAST;
}

You might prefer case statement

public Direction getOppositeDirection() {
    switch (this) {
        case NORTH: return SOUTH;
        case EAST:  return WEST;
        case SOUTH: return NORTH;
        case WEST:  return EAST;
        default: throw new Error();
    }
}
like image 39
Tom Hawtin - tackline Avatar answered Sep 23 '22 08:09

Tom Hawtin - tackline


You may also want to look in lambda for other case, but you will need to use a little cheat code because NORTH and EAST can't reference constant/field before they are defined:

import java.util.function.Supplier;

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

  private Supplier<Direction> opposite;

  private Direction() {
  }

  static { // initialize opposite field
    for (final Direction direction : Direction.values()) {
      direction.opposite = oppositeOf(direction);
    }
  }

  private static Supplier<Direction> oppositeOf(final Direction self) {
    switch (self) {
      case NORTH: return () -> SOUTH;
      case WEST:  return () -> EAST;
      case EAST:  return () -> WEST;
      case SOUTH: return () -> NORTH;
      default:    throw new IllegalStateException("Invalid opposite: " + self);
    }
  }

  public final Direction getOppositeDirection() {
    return opposite.get();
  }

  public static void main(final String[] args) {
    for (final Direction dir : Direction.values()) {
      System.out.println(dir + " is opposite of " + dir.getOppositeDirection());
    }
  }
}

This does the same than having abstract method, but is more concise (except here because you need to add more to the code): you read directly what is the opposite direction instead of having the verbose method declaration (it is another matter if you need to also add other methods).

Also, it will not create pseudo class for each enums constant (eg: Direction$1, Direction$2, ...) because there is no need to.

like image 37
NoDataFound Avatar answered Sep 24 '22 08:09

NoDataFound