5

我非常熟悉在其他语言中使用 Enums,但是我在 Java 中遇到了一些特殊用途的困难。

枚举的 Sun 文档大胆地指出:

“Java 编程语言的枚举比它们在其他语言中的对应物强大得多,它们只不过是美化的整数。”

好吧,这很花哨,但出于在 switch 语句中进行比较的原因,我需要为每个枚举提供一个常量数据类型表示。情况如下:我正在构建将代表给定空间的节点,或迷宫图中的“槽”,并且这些节点必须能够从代表迷宫的 2D 整数数组构造。这是我为 MazeNode 类获得的内容,目前是问题所在(switch 语句吠叫):

注意:由于 case 语句中的动态项,我知道此代码不起作用。它是为了说明我所追求的。

public class MazeNode
{
    public enum SlotValue
    {
        empty(0),
        start(1),
        wall(2),
        visited(3),
        end(9);

        private int m_representation;

        SlotValue(int representation)
        {
            m_representation = representation;
        }

        public int getRepresentation()
        {
            return m_representation;
        }
    }

    private SlotValue m_mazeNodeSlotValue;

    public MazeNode(SlotValue s)
    {
        m_mazeNodeSlotValue = s;
    }

    public MazeNode(int s)
    {

        switch(s)
        {
            case SlotValue.empty.getRepresentation():
                m_mazeNodeSlotValue = SlotValue.start;
                break;
            case SlotValue.end.getRepresentation():
                m_mazeNodeSlotValue = SlotValue.end;
                break;

        }
    }

    public SlotValue getSlotValue()
    {
        return m_mazeNodeSlotValue;
    }

}

所以代码用“case 表达式必须是常量表达式”来抱怨 switch 语句——我可以理解为什么编译器可能会遇到问题,因为从技术上讲它们是动态的,但我不确定采取什么方法来解决这个问题。有没有更好的办法?

底线是我需要 Enum 具有相应的整数值,以便与程序中传入的 2D 整数数组进行比较。

4

7 回答 7

5

你可以使用这样的东西:

import java.util.HashMap;
import java.util.Map;

public class MazeNode {

    public enum SlotValue {
        empty(0), start(1), wall(2), visited(3), end(9);

        protected int m_representation;

        SlotValue(int representation) {
            m_representation = representation;

        }

        private static final Map<Integer, SlotValue> mapping = new HashMap<Integer, SlotValue>();

        static {
            for (SlotValue slotValue : SlotValue.values()) {
                mapping.put(slotValue.m_representation, slotValue);
            }
        }

        public static SlotValue fromRepresentation(int representation) {
            SlotValue slotValue = SlotValue.mapping.get(representation);
            if (slotValue == null)
                // throw your own exception
                throw new RuntimeException("Invalid representation:" + representation);
            return slotValue;
        }
    }

    private SlotValue m_mazeNodeSlotValue;

    public MazeNode(SlotValue s) {
        m_mazeNodeSlotValue = s;
    }

    public MazeNode(int s) {
        m_mazeNodeSlotValue = SlotValue.fromRepresentation(s);

    }

    public SlotValue getSlotValue() {
        return m_mazeNodeSlotValue;
    }

    public static void main(String[] args) {
        MazeNode m = new MazeNode(2);
        System.out.println(m.getSlotValue());
        m = new MazeNode(9);
        System.out.println(m.getSlotValue());
    }

}
于 2009-06-27T07:09:36.547 回答
2

其他建议的另一种方法是您可以在枚举构造函数中输入值,而不必事后循环:

public class MazeNode {
    private static final Map<Integer, SlotValue> mapping = new HashMap<Integer, SlotValue>();

    enum SlotValue {
        empty(0),start(1),wall(2),visited(3),end(9);

        private final int m_representation;

        SlotValue(int representation) {
            m_representation = representation;
            mapping.put(Integer.valueOf(representation), this);
        }

    }

    SlotValue getSlotValue(int representation) {
        return mapping.get(Integer.valueOf(representation));
    }

}
于 2009-06-27T07:46:50.247 回答
1

似乎没有简单的方法可以做你想做的事。常量在声明时必须既是最终的又是赋值的;你不能在枚举成员中做的事情。

通过解决方案,我向 SlotValue 枚举添加了一个静态 decode() 方法。这会比较每个 SlotValue 的 m_representation 字段并返回第一个匹配项。它会起作用,而且它可能不是最有效的方法,但它只需要几行代码就可以完成这项工作。

public class MazeNode {
    public enum SlotValue {
        empty(0),
        start(1),
        wall(2),
        visited(3),
        end(9);

        private int m_representation;

        SlotValue(int representation) {
            m_representation = representation;
        }

        private static SlotValue decode( int in ) {
            for ( SlotValue slot : values() ) {
                if ( slot.m_representation == in ) {
                    return slot;
                }
            }
            return empty;
        }
    }

    private SlotValue m_mazeNodeSlotValue;

    public MazeNode(SlotValue s) {
        m_mazeNodeSlotValue = s;
    }

    public MazeNode(int s) {
        m_mazeNodeSlotValue = SlotValue.decode( s );
    }

    public SlotValue getSlotValue() {
        return m_mazeNodeSlotValue;
    }
}
于 2009-06-27T06:48:47.510 回答
1

我同意 olliej,您可能应该接受一个吸气剂。Java(与 C# 不同)不允许您在原语(在您的情况下为 int)和枚举之间进行转换。内部表示(此处为 m_representation)只是另一个字段,而不是可访问的常量。

这是好的(真正类型安全的)或坏的(更难序列化和反序列化等)取决于你如何看待它。下面的方法显然不如 switch 高效,但我相信这是避免冗余的唯一方法。

您可能已经意识到,最好尽可能将数据保持为枚举形式。

public enum SlotValue
{
    empty(0),
    start(1),
    wall(2),
    visited(3),
    end(9);

    private int m_representation;

    SlotValue(int representation)
    {
        m_representation = representation;
    }

    public static SlotValue fromInt(int intSerialization)
    {
        for(SlotValue sv : SlotValue.values())
            if(sv.m_representation == intSerialization)
                return sv;
        return null;
    }
}

private SlotValue m_mazeNodeSlotValue;

public MazeNode(SlotValue s)
{
    m_mazeNodeSlotValue = s;
}

public MazeNode(int s)
{
    m_mazeNodeSlotValue = SlotValue.fromInt(s);
}
于 2009-06-27T07:07:44.383 回答
0

所有语言(除了 JS,因为它很疯狂 :D 除外)都要求 switch 语句是常量表达式。

你想做什么?通过向 SlotValue 添加一个 getter,您可以轻松地从 SlotValue 转到 int。

于 2009-06-27T06:43:30.513 回答
0

考虑做这样的事情:

public class Node {
    public enum Slot {
        empty, start, wall, visited, end;
        static Slot fromInt(int s) {
            for (Slot slot : Slot.values())
                if (slot.ordinal() == s)
                    return slot;
            throw new RuntimeException("" + s + " is illegal value!");
        }
    }
    public Node(Slot slot) {
        this.slot = slot;
    }
    public Node(int s) {
        this(Slot.fromInt(s));
        switch(slot) {
            case empty: /* special stuff for empty */ break;
            case start: /* special stuff for start */ break;
            /* ... */
        }
    }
    private Slot slot;
}
于 2009-06-27T07:18:33.003 回答
-2

我不确定 SlotValue 在您的程序中的目的是什么,但似乎您没有使用它们的全部功能。您可以在枚举实例上调用方法或查询它们的状态。因此,您将枚举值的开关转换为方法调用/状态查询,如下所示。

旁注:一旦你习惯了这种思维(这与 C 枚举引起的思维完全不同),你就会意识到代码中对“幻数”的需求要少得多。您只需指定枚举常量并调用其上的方法,而不是指定一个值然后赋予它一些含义。具体来说,我的感觉是没有真正需要将每个枚举实例与一个值相关联(空为 0,开始为 1,等等)。

无论如何,这是代码:

public class MazeNode
{
   public enum SlotValue
   {
       empty(0),
       start(1),
       wall(2),
       visited(3),
       end(9);

       private int m_representation;

       SlotValue(int representation)
       {
           m_representation = representation;
       }

       public int getRepresentation()
       {
           return m_representation;
       }

       private SlotValue next = null;

       static
       {
          empty.next = start;
          end.next = end;
       }
   }


   private SlotValue m_mazeNodeSlotValue;

   public MazeNode(SlotValue s)
   {
       m_mazeNodeSlotValue = s;
   }

   public MazeNode(int s)
   {
       m_mazeNodeSlotValue = SlotValue.values()[s].next;
   }

   public SlotValue getSlotValue()
   {
       return m_mazeNodeSlotValue;
   }
}
于 2009-06-27T07:14:21.233 回答