【问题标题】:Is it possible to give Enums instance variables of their own type in Groovy?是否可以在 Groovy 中为 Enums 提供自己类型的实例变量?
【发布时间】:2012-07-02 22:57:36
【问题描述】:

我正在用 Groovy 制作一个文本冒险游戏作为一种练习,但我遇到了一个奇怪的错误。

现在,我有一个enum,用于指示玩家可以去的方向,目前包含北、南、东、西、上和下。

我有一个 Room 类,其中包含一个 Map 的其他连接房间及其方向。当我在某个DirectionRoom 添加到另一个Room 时,我希望能够在相反 方向将当前Room 添加到另一个Room

例如:如果我添加从房间 1 到房间 2 向北的连接,我希望能够同时添加从房间 2 到房间 1 向南的连接。

目前,我正在尝试使用名为 Directionenum 和附加的实例变量 oppositeDirection 类型)来实现这一点。这是不允许的吗?我没有收到编译器错误或任何东西,但我似乎无法让它工作。

这是完整的enum 声明:

public enum Direction {
    North(South), South(North), East(West), West(East), Up(Down), Down(Up)
    private Direction opposite
    Direction(Direction d){
        opposite = d
    }
    public opposite(){
        return opposite
    }
}

这是我调用它的方法:

public void addConnection(Direction d, Spot spot){
    connections[d] = spot
    spot.connections[d.opposite()] = this
}

其中connectionspublic Map<Direction, Spot>

在这种情况下,一个条目被添加到connections,如下所示:

null:Spot@some_hexadecimal_representation

任何帮助都会很棒。谢谢!

【问题讨论】:

    标签: java groovy enums


    【解决方案1】:

    Groovy 似乎绕过了 Java 中的 a compilation error

    Main.java:2: illegal forward reference
        North(South), South(North), East(West), West(East), Up(Down), Down(Up);
              ^
    Main.java:2: illegal forward reference
        North(South), South(North), East(West), West(East), Up(Down), Down(Up);
                                         ^
    Main.java:2: illegal forward reference
        North(South), South(North), East(West), West(East), Up(Down), Down(Up);
                                                               ^
    3 errors
    

    groovy 编译器并没有抱怨,而是将需要前向声明的枚举值初始化为null

    public enum Direction {
        North(South), South(North), East(West), West(East), Up(Down), Down(Up)
        Direction(Direction d){
            println "opposite of $this is $d"
        }
    }
    
    Direction.South // Force enum instantiation in GroovyConsole.
    

    Outputs:

    opposite of North is null
    opposite of South is North
    opposite of East is null
    opposite of West is East
    opposite of Up is null
    opposite of Down is Up
    

    在 Java 中似乎工作得很好的一个解决方案是 adding a static block on the Direction class 来初始化 opposite 值。翻译成 Groovy 的话,就是:

    enum Direction {
        North, South, East, West, Up, Down
        private Direction opposite
        Direction getOpposite() { opposite }
    
        static {
            def opposites = { d1, d2 -> d1.opposite = d2; d2.opposite = d1 }
            opposites(North, South)
            opposites(East, West)
            opposites(Up, Down)
        }
    }
    
    Direction.values().each { 
        println "opposite of $it is $it.opposite"
    }
    

    现在prints the correct values:

    opposite of North is South
    opposite of South is North
    opposite of East is West
    opposite of West is East
    opposite of Up is Down
    opposite of Down is Up
    

    更新

    Another,也许更直接,解决方案可以使用枚举上的方向索引来找到相反的:

    public enum Direction {
        North(1), South(0), East(3), West(2), Up(5), Down(4)
        private oppositeIndex
        Direction getOpposite() { 
            values()[oppositeIndex]
        }
        Direction(oppositeIndex) { 
            this.oppositeIndex = oppositeIndex
        }
    }
    

    但我发现第一个更清晰,因为它不需要那些神奇的索引数字呵呵。

    更新 2

    现在,我可能对这里的高尔夫球场有所了解,但你可以得到相反的方向without the need of an extra field,只需使用枚举值'ordinal()(它们的索引):

    enum Direction {
        North, South, East, West, Up, Down
        Direction getOpposite() { 
            values()[ordinal() + ordinal() % 2 * -2 + 1]
        }
    }
    

    它并不像看起来那么可怕!偶数方向(北、东、上)返回ordinal() + 1 的方向作为它们的相反方向,而奇数方向(其他方向)返回ordinal() - 1 的方向。当然它在很大程度上依赖于枚举中元素的顺序,但是,你不喜欢简洁吗? =D

    【讨论】:

    • 很好的解决方案!这可以在我的机器上顺利编译和运行,并且非常易读。谢谢。
    • @BenjaminKovach 如果您不想要这么多的可读性(或者您想为代码的未来维护者留下一个小谜团),请查看我对答案 xD 的第二次更新
    • +1 不错的解决方案!如果您只想计算一次相反数,您也可以用@Lazy opposite = { values()[ordinal() + ordinal() % 2 * -2 + 1] }() 替换getOpposite 方法(尽管在这种情况下,这是矫枉过正);-)
    • @tim_yates 事实证明,你甚至不需要关闭! @Lazy opposite = values()[ordinal() + ordinal() % 2 * -2 + 1] 工作得很好!
    【解决方案2】:

    您可以通过在闭包中传递相反的结果并在需要相反时调用闭包来推迟对相反的评估:

    public enum Direction {
        North({South}), South({North}), East({West}), West({East}), Up({Down}), Down({Up})
        private def opp
        Direction(opp) {
            this.opp = opp
        }
        public opposite() {
            return opp()
        }
    }
    public static void main(String[] args) {
        Direction.each { d ->
            println "${d} ... ${d.opposite()}"
        }
    }
    

    输出:

    北...南 南北 东西 西...东 上...下 向下...向上

    【讨论】:

      【解决方案3】:

      看起来有一半的Directions 在调用枚举构造函数时没有被初始化。也就是说,当您调用North(South) 时,South 尚未初始化。它是下一个。

      您陷入了鸡/蛋悖论,其中 所有 枚举常量必须在 其中一个 之前初始化。似乎您需要重新组织一些代码来解决这个问题。我可能会建议:

      public enum Direction {
          North(1), South(~1), East(2), West(~2), Up(4), Down(~4);
      
          private int value;
      
          Direction(int d){
              value = d;
          }
      
          private int getValue() {
              return value;
          }
      
          public Direction opposite(){
              for (Direction d : Direction.values()) {
                  if (value == ~(d.getValue())) 
                      return d;
              }
              return null;
          }
      }
      

      这利用按位运算符~ 来区分对立面。

      【讨论】:

      • 啊,有道理。不过,当我尝试迭代 Directions 时,直接复制/粘贴这段代码会给我一个 java.lang.ExceptionInInitializerError。我不太热衷于这意味着什么。有什么想法吗?
      • @Benjamin 它的意思和它所说的差不多。您可以在违规行周围放置一个 try/catch 块并捕获错误,然后在错误上调用 getException() 吗?这应该会给你更多关于到底出了什么问题的信息。
      • @Benjamin 我解决了这个问题,这是我的错误。不知道这个答案是否还有帮助,不过代码已经更正了。
      猜你喜欢
      • 2022-08-10
      • 1970-01-01
      • 1970-01-01
      • 2016-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      • 1970-01-01
      相关资源
      最近更新 更多