2009-06-27 3 views
5

다른 언어로 열거 형을 사용하는 것에 익숙하지만 특정 용도로 Java에서 어려움을 겪고 있습니다.Java의 열거 형 표현에 대한 switch 문

열거 형의 Sun 설명서 대담 상태 :

. "자바 프로그래밍 언어 열거되어 훨씬 더 강력한 영광 정수보다는 조금 더 많은 다른 언어에서의 대응보다"

글쎄, 그건 멋쟁이 야.하지만 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 문에 불만 - 컴파일러가 문제가 이유를 기술적으로 동적이기 때문에 내가 볼 수 있습니다,하지만 난 모르겠어요 무엇을 이를 해결하기위한 접근법. 더 좋은 방법이 있습니까?

결론은 프로그램의 정수 2 차원 배열에 대한 비교를 위해 해당 정수 값을 갖기 위해 열거 형을 필요로합니다.

답변

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

} 
+0

이것은 Matthew가 위에 말한 것과 거의 같지만 좋은 해결책 인 것 같습니다. –

+0

Ray Tayek의 솔루션에서 스로인 예외가 발생했습니다. –

+0

니스. 실제로 매핑 및 정적 생성자를 열거 형 내부로 이동할 수 있습니다. –

0

모든 언어 (JS가 미쳐 있기 때문에 제외 : D)는 switch 문이 상수 표현 인 것을 요구합니다.

무엇을하려하십니까? SlotValue에 getter를 추가하여 SlotValue에서 int로 쉽게 이동할 수 있습니다.

+0

예 getter를 추가 할 수는 있지만 iss를 처리하지 못합니다. ue : 특정 SlotValue 열거 형에서 작동하는 switch 문에서 비교할 대상이 있어야합니다. 이것은 자바가 불평하는 곳이다. –

+0

내 말은, case 문을 원시 정수 값으로 사용할 수는 있지만, 임의의 정수 집합과 반대로 Enum이라는 목적을 상실 할 수 있습니다. Enum 값을 정수로 매핑하는 방법을 찾고 있습니다. –

+0

getter를 사용하여 문제를보다 명확하게 반영하도록 코드를 업데이트했습니다. 분명히 스위치에서 case 문으로 메서드를 사용할 수는 없지만, 필자는이를 필자가 제공 한 그림으로 제공하고 있습니다. –

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

해당 case 문은 일정해야하지만 제안 된 솔루션으로 오류가 해결되지 않는다는 사실을 알고 있어야합니다. switch 문은 여전히 ​​항목 항목이 일정하지 않다는 것에 대해 불만을 표시합니다. –

+0

jove가 맞습니다. 내 대답을 더 잘 편집 할 것입니다. – banjollity

+0

Heh, 고마워요. 다른 사람들이 위에서 제안한 내용입니다. 사용하겠습니다. –

1

나는 아마도 당신이 getter를 받아 들여야한다고 olliej입니다. 자바 (C#과 달리)는 당신이 원시 (당신의 경우 int)와 enum 사이에서 캐스트하는 것을 허용하지 않는다. 내부 표현 (여기에서 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); 
} 
+0

여기에 게터보다 조금 더 많은 것이 있습니다 ... 작동하지만, 고마워요. –

-2

나는 당신의 프로그램 SlotValue의 목적은 무엇 확실하지 않다 그러나 당신이 자신의 모든 힘을 사용하지 않는 것 같다. enum 인스턴스에서 메소드를 호출하거나 그 상태를 질의 할 수 있습니다. 따라서 아래와 같이 열거 형 값의 전환을 메소드 호출/상태 조회로 변환합니다.

사이드 노트 : 이런 사고 방식 (C의 enum에 의해 유도 된 생각과는 상당히 다른)에 익숙해지면 코드에 "매직 넘버"가 훨씬 덜 필요하다는 것을 알게됩니다. 값을 지정하고 어떤 의미를 부여하는 대신 enum 상수를 지정하고 그것에 메소드를 호출하면됩니다. 구체적으로, 열거 형 인스턴스 각각을 값 (빈 값은 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; 
    } 
} 
+0

잘못되었습니다. "end"의 경우, MazeNode 생성자에서 SlotValue.values ​​() [9]를 얻게되어 ArrayIndexOutOfBoundsException을 얻을 수 있습니다. –

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

} 
+0

더 나은 성능을 가지고 있기 때문에 나는이 간단한 것을 좋아하지만, 다시 5 항목의 반복 (더 이상 없을 것입니다)은 모두 비싸지 않습니다. :) –