【问题标题】:Switch Statement for Enum Value Representations in JavaJava 中枚举值表示的 Switch 语句
【发布时间】:2009-06-27 06:36:55
【问题描述】:

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

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

}

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

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

【问题讨论】:

    标签: java enums switch-statement


    【解决方案1】:

    你可以这样使用:

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

    【讨论】:

    • 这或多或少是 Matthew 上面所说的,听起来是个不错的解决方案。谢谢
    • 从 Ray Tayek 的解决方案中抛出的被盗异常。
    • 不错。您实际上可以在枚举中移动映射和静态构造函数。
    【解决方案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));
        }
    
    }
    

    【讨论】:

    • 我确实喜欢这个简单的方法,因为它具有更好的性能,但是我再次认为 5 个项目的循环(永远不会再有)也不是那么昂贵。 :)
    【解决方案3】:

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

    通过一种解决方案,我向 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;
        }
    }
    

    【讨论】:

    • 意识到 case 语句必须保持不变,但您提出的解决方案并不能解决错误。 switch 语句仍然说抱怨 case 项不是恒定的。
    • 天哪,你是对的。我会用更好的东西编辑我的答案。
    • 呵呵,谢谢 - 这是其他人在上面提出的,我想我会使用它。
    【解决方案4】:

    我支持 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);
    }
    

    【讨论】:

    • 这里不仅仅是一个吸气剂......但它有效,所以我感谢你先生。
    【解决方案5】:

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

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

    【讨论】:

    • 是的,我可以添加一个 getter,但这并不能解决问题:在特定 SlotValue Enum 类型上运行的 switch 语句中有一些要比较的东西。这就是 java 抱怨的地方。
    • 我的意思是,我可以很好地将 case 语句作为原始整数值,但这违背了它作为枚举的目的,而不是任意整数集。我正在寻找一种将枚举值映射到整数的方法。
    • 更新了代码以更清楚地反映问题,并带有一个吸气剂。显然,不能将方法用作 switch 中的 case 语句 - 但我提供它是为了说明我所追求的。
    • 旁注:按照您的标准,PHP 在这里也是“疯狂的”......呵呵。不过我知道你的意思。
    【解决方案6】:

    考虑做这样的事情:

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

    【讨论】:

      【解决方案7】:

      我不确定 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;
         }
      }
      

      【讨论】:

      • 这是错误的。对于“结束”,您将在 MazeNode 构造函数中获得 SlotValue.values()[9],这将为您提供 ArrayIndexOutOfBoundsException。
      猜你喜欢
      • 1970-01-01
      • 2011-12-27
      • 1970-01-01
      • 2017-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多