java 非法前向引用和枚举

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5678309/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 12:15:18  来源:igfitidea点击:

Illegal Forward Reference and Enums

javaenums

提问by MattLBeck

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. to get the opposite edge of a tile, I want to be able to just type TOP.opposite(). However, when using enums to define these edges I end up having to forward reference at least two of them in the contstructor:

我正在用 Java 编写一个由瓷砖网格组成的游戏。我不想能够直观地定义瓷砖的边缘以及它们如何相互关联,例如,为了获得瓷砖的相对边缘,我希望能够只输入TOP.opposite(). 但是,当使用枚举定义这些边时,我最终不得不在构造函数中前向引用其中至少两个:

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;
   }
}

Is there any way of getting round this problem using enums which is just as simple?

有没有什么方法可以使用同样简单的枚举来解决这个问题?

回答by Peter Lawrey

You can do this which is not as intuitive.

你可以这样做,这并不直观。

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;
    }
}

回答by Vitaliy Oliynyk

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();
}

回答by eyelash

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;
    }
}

回答by Hymantrades

You can also make use of an static innerclassinside 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:)

这里被盗:)

回答by BalusC

Here's another way

这是另一种方式

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's solution is however more efficient and compiletime safe.

然而,Peter Lawrey 的解决方案更高效且编译时安全。

回答by Jan Zyka

You can create a static Mapwhere key is the original enum and the value the opposite edge. Initialize it in a static block and the return the mapping from the opposite()method.

您可以创建一个静态Map,其中 key 是原始枚举,而 value 是相反的边缘。在静态块中初始化它并从opposite()方法返回映射。

private static Map<Edge, Edge> oppostiteMapping;

static {
  oppositeMapping = new EnumMap<Edge, Edge>();
  oppositeMapping.put(TOP, BOTTOM);
  ...
}

public Edge opposite() {
    return oppositeMapping.get(this);
} 

EDIT:as proposed in comment better to use EnumMap, so I upgraded accordingly

编辑:正如评论中建议的那样更好地使用 EnumMap,所以我相应地升级

Btw. this approach is generally useful when you create something like static fromString()method etc.

顺便提一句。当您创建静态fromString()方法等时,这种方法通常很有用。

回答by Jeff Foster

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");
        }
    }
}

回答by Péter T?r?k

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:

您可以改用内部 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);
  }
}

回答by Mark Jeronimus

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:

虽然不鼓励使用 ordinal 是因为它是脆弱的,但在与它定义的相同的枚举中使用 ordinal 是不那么脆弱的,并且在这里通过评论进一步减轻了它的影响。虽然上面的例子很简单,但下一个例子就不那么简单了。比较原始方式和使用序数的方式:

From 98 lines:

从 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);
        }
    }
}

to 19 lines:

到 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()];
    }
}

回答by vitali_y

I preferred this:

我更喜欢这个:

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;
   }
}