Инструкция Switch для представления значений Enum в Java - PullRequest
5 голосов
/ 27 июня 2009

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

В документации Sun для Enums смело говорится:

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

Что ж, это здорово, но мне нужно иметь постоянное представление типов данных для каждого из перечислений, по причинам сравнения в операторе switch. Ситуация выглядит следующим образом: я создаю узлы, которые будут представлять данное пространство, или «слот» в графе лабиринта, и эти узлы должны быть в состоянии построить из двумерного целочисленного массива, который представляет лабиринт. Вот что у меня есть для класса 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;
    }

}

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

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

Ответы [ 7 ]

5 голосов
/ 27 июня 2009

Вы можете использовать что-то вроде этого:

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

}
2 голосов
/ 27 июня 2009

Альтернативный подход к другим предложениям заключается в том, что вы можете вводить значения в конструкторы enum, а не выполнять циклическое повторение:

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

}
1 голос
/ 27 июня 2009

Я с 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);
}
1 голос
/ 27 июня 2009

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

В качестве решения я добавил статический метод 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 голосов
/ 27 июня 2009

рассмотрите возможность сделать что-то вроде этого:

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;
}
0 голосов
/ 27 июня 2009

Я не уверен, какова цель 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 голосов
/ 27 июня 2009

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

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

...