我正在用java编写一个由瓷砖网格组成的游戏。我不希望能够直观地定义瓷砖的边缘以及它们之间的相互关系,例如:要获得图块的相反边缘,我希望能够只键入TOP.opposite()
。但是,当使用枚举来定义这些边时,我最终必须在contstructor中至少引用其中两个:
public enum Edge {
TOP(Edge.BOTTOM), //illegal forward reference
BOTTOM(Edge.TOP),
LEFT(Edge.RIGHT), //illegal forward reference
RIGHT(Edge.LEFT);
private Edge opposite;
private Edge(Edge opp){
this.opposite = opp;
}
public Edge opposite(){
return this.opposite;
}
}
有没有办法使用枚举来解决这个问题,这很简单?
答案 0 :(得分:72)
你可以这样做,这不是那么直观。
public enum Edge {
TOP, BOTTOM, LEFT, RIGHT;
private Edge opposite;
static {
TOP.opposite = BOTTOM;
BOTTOM.opposite = TOP;
LEFT.opposite = RIGHT;
RIGHT.opposite = LEFT;
}
public Edge opposite(){
return this.opposite;
}
}
答案 1 :(得分:16)
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();
}
答案 2 :(得分:12)
public enum Edge {
TOP,
BOTTOM(Edge.TOP),
LEFT,
RIGHT(Edge.LEFT);
private Edge opposite;
private Edge() {
}
private Edge(Edge opp) {
this.opposite = opp;
opp.opposite = this;
}
public Edge opposite() {
return this.opposite;
}
}
答案 3 :(得分:6)
这是另一种方式
public enum Edge {
TOP("BOTTOM"),
BOTTOM("TOP"),
LEFT("RIGHT"),
RIGHT("LEFT");
private String opposite;
private Edge(String opposite){
this.opposite = opposite;
}
public Edge opposite(){
return valueOf(opposite);
}
}
然而,Peter Lawrey的解决方案更有效,编译时更安全。
答案 4 :(得分:5)
您还可以在枚举中使用 static innerclass :
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;
}
}
从here被盗}
答案 5 :(得分:3)
您可以创建一个静态Map
,其中key是原始枚举,值是相反的边缘。在静态块中初始化它,并从opposite()
方法返回映射。
private static Map<Edge, Edge> oppostiteMapping;
static {
oppositeMapping = new EnumMap<Edge, Edge>();
oppositeMapping.put(TOP, BOTTOM);
...
}
public Edge opposite() {
return oppositeMapping.get(this);
}
编辑:在评论中建议更好地使用EnumMap,所以我相应升级了
顺便说一下。当你创建类似静态fromString()
方法等的东西时,这种方法通常很有用。
答案 6 :(得分:3)
您可以定义一个类似于下面的方法。
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");
}
}
}
答案 7 :(得分:1)
您可以使用内部Map来定义这些关联。如果在初始化Map时,您已经创建了所有枚举值:
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);
}
}
答案 8 :(得分:1)
我的方法是使用序数。这是一个简单的例子,但是对于更复杂的例子,请参见下文。
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()];
}
}
虽然不鼓励使用序数是脆弱的,但在其定义的同一枚举中使用序数不那么脆弱,并且在这里通过评论进一步减轻了它。虽然上面的例子非常简单,但下一个例子却不那么简单。比较原始方式和使用序数的方式:
从98行开始:
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);
}
}
}
到19行:
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()];
}
}
答案 9 :(得分:0)
我更喜欢这个:
public enum Edge {
TOP,
BOTTOM,
LEFT,
RIGHT;
private Link link;
private Link getLink() {
if (link == null) {
link = Link.valueOf(name());
}
return link;
}
public Edge opposite() {
return getLink().opposite();
}
}
public enum Link {
TOP(Edge.BOTTOM),
BOTTOM(Edge.TOP),
LEFT(Edge.RIGHT),
RIGHT(Edge.LEFT);
private Edge opposite;
private Link(Edge opp) {
this.opposite = opp;
}
public Edge opposite() {
return this.opposite;
}
}
答案 10 :(得分:-1)
使用Java 8 lambdas:
public enum Edge {
TOP(() -> Edge.BOTTOM),
BOTTOM(() -> Edge.TOP),
LEFT(() -> Edge.RIGHT),
RIGHT(() -> Edge.LEFT);
private Supplier<Edge> opposite;
private Edge(Supplier<Edge> opposite) {
this.opposite = opposite;
}
public Edge opposite() {
return opposite.get();
}
}