2009-06-27 2 views
5

Я очень хорошо знаком с использованием Enums на других языках, но у меня с некоторыми трудностями возникает Java.Заявление о переключении для представлений значений контировки в Java

документация ВС для Перечисления смело заявляет:

«язык программирования Java перечислений являются гораздо более мощным, чем их аналоги на других языках, которые немного больше, чем прославил целые числа.»

Ну, это денди, но мне нужно иметь постоянное представление типа данных для каждого из Enums для сравнения в инструкции switch. Ситуация такова: я строю узлы, которые будут представлять заданное пространство или «слот» в графе лабиринта, и эти узлы должны быть построены из массива 2D целого, который представляет лабиринт. Вот что у меня есть для класса MazeNode, в котором в настоящее время проблема (справка оператора):

ПРИМЕЧАНИЕ. Я знаю, что этот код не работает из-за динамического элемента в инструкции 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; 
    } 

} 

Так код жалуется на заявление переключателя с «случае выражения должны быть константными выражениями» - Я могу понять, почему компилятор может возникнуть проблемы, так как технически они являются динамическими, но я не уверен, что подход к решению этого. Есть ли способ лучше?

В нижней строке мне нужно, чтобы Enum имел соответствующие целочисленные значения для сравнения с входящим 2D-массивом целых чисел в программе.

ответ

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

Это более или менее то, что сказал Матфей выше, что звучит как хорошее решение. Спасибо –

+0

Украденное исключение, брошенное из решения Рэй Тайек. –

+0

Ницца. Вы можете переместить отображение и статический конструктор внутри перечисления. –

0

Все языки (за исключением JS, потому что это безумно: D) требуют, чтобы операторы switch были постоянными выражениями.

Что вы пытаетесь сделать? Вы можете легко перейти от SlotValue к int, добавив getter в SlotValue.

+0

Да, я мог бы добавить геттер, но это не касается проблема: иметь что-то, что можно сравнить с выражением switch, работающим на конкретном типе SlotValue Enum. Здесь жаба жалуется. –

+0

Я имею в виду, что я мог бы просто иметь аргументы case как необработанные целочисленные значения, но этот вид побеждает цель этого Enum, в отличие от произвольного набора целых чисел. Я ищу способ сопоставить значения Enum с целыми числами. –

+0

Обновленный код, который будет более четко отражать проблему, с геттером. Очевидно, нельзя использовать метод в качестве оператора case в коммутаторе, но я предоставляю это как иллюстрацию того, что мне нужно. –

1

Похоже, что нет простого способа сделать то, что вы хотите. Константы должны быть как окончательными, так и назначенными, когда они объявлены; то, что вы не можете сделать в члене перечисления.

В качестве решения я добавил метод static decode() для перечисления SlotValue. Это сравнивает каждое поле m_representation каждого SlotValue и возвращает первое совпадение. Он будет работать, и это может быть не самый эффективный подход, но он выполняет эту работу только в нескольких строках кода.

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

Клянусь, ты прав. Я отредактирую свой ответ с чем-то лучшим. – banjollity

+0

Хех, спасибо - вот что предложили другие, и я думаю, что буду использовать его. –

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

Здесь немного больше, чем просто геттер ... но он работает, поэтому я благодарю вас, сэр. –

-2

Я не уверен, что цель SlotValue в вашей программе, но, кажется, что вы не используете их на полную мощность. Вы можете вызывать методы для экземпляров enum или запрашивать их состояние. Таким образом, вы переводите переключатель на значение 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» вы получите SlotValue.values ​​() [9] в конструкторе MazeNode, который даст вам исключение 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 предметов (больше никогда не будет) не так уж и дорога. :) –