非法前向引用和枚举

发布于 2024-11-01 09:25:49 字数 530 浏览 1 评论 0原文

我正在用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;
   }
}

是否有任何方法可以使用同样简单的枚举来解决这个问题?

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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

西瑶 2024-11-08 09:25:49

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

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

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;
    }
}
虫児飞 2024-11-08 09:25:49
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();
}
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();
}
寂寞美少年 2024-11-08 09:25:49
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;
    }
}
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;
    }
}
青芜 2024-11-08 09:25:49

您还可以在枚举中使用静态内部类:

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

此处 :)

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 :)

梦幻的心爱 2024-11-08 09:25:49

添加方法 opposite 以返回枚举对象

您可以定义一个方法 opposite()

在现代 Java 中,开关表达式

在现代 Java 中,我们可以使用 开关表达式。编译器确保我们涵盖了所有可能的情况。

enum Edge
{
    TOP, BOTTOM, LEFT, RIGHT;

    public Edge opposite ( )
    {
        return switch ( this )
                {
                    case TOP -> BOTTOM;
                    case BOTTOM -> TOP;
                    case LEFT -> RIGHT;
                    case RIGHT -> LEFT;
                };
    }
}

用法:

System.out.println( Edge.TOP.opposite() );

底部

在早期的 Java 中,switch

在早期的 Java 中,使用以下代码中所示的语法。

请注意,需要使用 default 大小写,以防您向枚举添加元素或无意中从 switch 中删除大小写。

public enum Edge {
    TOP,
    BOTTOM,
    LEFT,
    RIGHT;

    public Edge opposite() {
        switch (this) {
            case TOP:
                return BOTTOM;
            case BOTTOM:
                return TOP;
            case LEFT:
                return RIGHT;
            case RIGHT:
                return LEFT;
            default:
                throw new RuntimeException("Oh dear");
        }
    }
}

Add a method opposite to return enum object

You could just define a method, opposite().

In modern Java, switch expression

In modern Java, we can use a switch expression. The compiler ensures that we have covered all possible cases.

enum Edge
{
    TOP, BOTTOM, LEFT, RIGHT;

    public Edge opposite ( )
    {
        return switch ( this )
                {
                    case TOP -> BOTTOM;
                    case BOTTOM -> TOP;
                    case LEFT -> RIGHT;
                    case RIGHT -> LEFT;
                };
    }
}

Usage:

System.out.println( Edge.TOP.opposite() );

BOTTOM

In earlier Java, switch

In older Java, use syntax seen in the following code.

Notice the need for a default case, in case you ever add an element to the enum or you inadvertently delete a case from the switch.

public enum Edge {
    TOP,
    BOTTOM,
    LEFT,
    RIGHT;

    public Edge opposite() {
        switch (this) {
            case TOP:
                return BOTTOM;
            case BOTTOM:
                return TOP;
            case LEFT:
                return RIGHT;
            case RIGHT:
                return LEFT;
            default:
                throw new RuntimeException("Oh dear");
        }
    }
}
一世旳自豪 2024-11-08 09:25:49

然而,

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 的解决方案是另一种更高效且编译时安全的方法。

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.

寒江雪… 2024-11-08 09:25:49

您可以创建一个静态Map,其中键是原始枚举,值是相对的边缘。在静态块中初始化它并从 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() 方法等时,这种方法通常很有用。

You can create a static Map where 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.

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

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

最后的乘客 2024-11-08 09:25:49

您可以使用内部映射来定义这些关联。如果在初始化映射时,您已经创建了所有枚举值,则此方法有效:

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

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);
  }
}
╰◇生如夏花灿烂 2024-11-08 09:25:49

我的方法是使用序数。这是一个简单的示例,但对于更复杂的示例,请参见下文。

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

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()];
    }
}
思念满溢 2024-11-08 09:25:49

我更喜欢这个:

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

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;
   }
}
看春风乍起 2024-11-08 09:25:49

使用 Java 8 lambda:

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

With 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();
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文