I\'m programming a game in java which is made up of a grid of tiles. I wan\'t to be able to inuitively define the edges of the tiles and how they relate to each other, e.g.
You can also make use of an static innerclass inside the enum:
public enum EnumTest
{
NORTH( Orientation.VERTICAL ),
SOUTH( Orientation.VERTICAL ),
EAST( Orientation.HORIZONTAL ),
WEST( Orientation.HORIZONTAL );
private static class Orientation
{
private static final String VERTICAL = null;
private static final String HORIZONTAL = null;
}
}
Stolen from here :)
You could just define a method similar to the one below.
public enum Edge {
TOP,
BOTTOM,
LEFT,
RIGHT;
public Edge opposite() {
switch (this) {
case TOP:
return Edge.BOTTOM;
case BOTTOM:
return Edge.TOP;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT;
default:
throw new RuntimeException("Oh dear");
}
}
}
My method is by using ordinal. This is a simple example, but for a much more complex example see below.
public enum Edge {
// Don't change the order! This class uses ordinal() in an arithmetic context.
TOP, // = 0
LEFT, // = 1
RIGHT, // = 2
BOTTOM; // = 3
public Edge other() {
return values()[3 - ordinal()];
}
}
Although using ordinal is discouraged for being fragile, using ordinal in the same enum as it's defined in is less fragile, and it's further mitigated here with a comment. Though the example above is quite trivial, the next example is less so. Compare the original way and the way using ordinal:
From 98 lines:
public enum Axes {
NONE,
HORIZONTAL,
VERTICAL,
BOTH;
public Axes add(Axes axes) {
switch (axes) {
case HORIZONTAL:
if (this == NONE)
return HORIZONTAL;
if (this == VERTICAL)
return BOTH;
break;
case VERTICAL:
if (this == NONE)
return VERTICAL;
if (this == HORIZONTAL)
return BOTH;
break;
case BOTH:
return BOTH;
default:
throw new AssertionError(axes);
}
return this;
}
public Axes remove(Axes axes) {
switch (axes) {
case HORIZONTAL:
if (this == HORIZONTAL)
return NONE;
if (this == BOTH)
return VERTICAL;
break;
case VERTICAL:
if (this == VERTICAL)
return NONE;
if (this == BOTH)
return HORIZONTAL;
break;
case BOTH:
return NONE;
default:
throw new AssertionError(axes);
}
return this;
}
public Axes toggle(Axes axes) {
switch (axes) {
case NONE:
return this;
case HORIZONTAL:
switch (this) {
case NONE:
return HORIZONTAL;
case HORIZONTAL:
return NONE;
case VERTICAL:
return BOTH;
case BOTH:
return VERTICAL;
default:
throw new AssertionError(axes);
}
case VERTICAL:
switch (this) {
case NONE:
return VERTICAL;
case HORIZONTAL:
return BOTH;
case VERTICAL:
return NONE;
case BOTH:
return HORIZONTAL;
default:
throw new AssertionError(axes);
}
case BOTH:
switch (this) {
case NONE:
return BOTH;
case HORIZONTAL:
return VERTICAL;
case VERTICAL:
return HORIZONTAL;
case BOTH:
return NONE;
default:
throw new AssertionError(axes);
}
default:
throw new AssertionError(axes);
}
}
}
to 19 lines:
public enum Axes {
// Don't change the order! This class uses ordinal() as a 2-bit bitmask.
NONE, // = 0 = 0b00
HORIZONTAL, // = 1 = 0b01
VERTICAL, // = 2 = 0b10
BOTH; // = 3 = 0b11
public Axes add(Axes axes) {
return values()[ordinal() | axes.ordinal()];
}
public Axes remove(Axes axes) {
return values()[ordinal() & ~axes.ordinal()];
}
public Axes toggle(Axes axes) {
return values()[ordinal() ^ axes.ordinal()];
}
}
You could use an internal Map instead to define these associations. This works if at the point of initializing the Map, you already have all enum values created:
public enum Edge {
TOP,
BOTTOM,
LEFT,
RIGHT;
private static final Map<Edge, Edge> opposites =
new EnumMap<Edge, Edge>(Edge.class);
static {
opposites.put(TOP, BOTTOM);
opposites.put(BOTTOM, TOP);
opposites.put(LEFT, RIGHT);
opposites.put(RIGHT, LEFT);
}
public Edge opposite(){
return opposites.get(this);
}
}
enum Edge {
TOP {
@Override
public Edge opposite() {
return BOTTOM;
}
},
BOTTOM {
@Override
public Edge opposite() {
return TOP;
}
},
LEFT {
@Override
public Edge opposite() {
return RIGHT;
}
},
RIGHT {
@Override
public Edge opposite() {
return LEFT;
}
};
public abstract Edge opposite();
}