我非常熟悉在其他语言中使用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整数数组进行比较。
答案 0 :(得分: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());
}
}
答案 1 :(得分: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));
}
}
答案 2 :(得分: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;
}
}
答案 3 :(得分:1)
我和olliej说你应该接受一个吸气剂。 Java(与C#不同)不允许您在基元(在您的情况下为int)和枚举之间进行转换。内部表示(此处为m_representation)只是另一个字段,而不是可访问的常量。
这很好(真正的类型安全)或坏(更难序列化和反序列化等)取决于你如何看待它。下面的方法显然不如交换机有效,但我相信这是避免冗余的唯一方法。
正如您可能已经意识到的那样,最好尽可能地将数据保持为枚举形式。
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);
}
答案 4 :(得分:0)
所有语言(JS除外,因为它是疯狂的:D)要求switch语句是常量表达式。
你想做什么?通过向SlotValue添加一个getter,您可以轻松地从SlotValue转到int。
答案 5 :(得分: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;
}
答案 6 :(得分:-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;
}
}