5

私は他の言語で列挙型を使用することに非常に精通していますが、特定の用途で 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 配列と比較するための対応する整数値が必要であるということです。

4

7 に答える 7

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

}
于 2009-06-27T07:09:36.547 に答える
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));
    }

}
于 2009-06-27T07:46:50.247 に答える
1

あなたが望むことをする簡単な方法はないようです。定数は、宣言時に final であり、代入されている必要があります。列挙型のメンバーではできないこと。

解決策として、静的な decode() メソッドを SlotValue 列挙型に追加しました。これにより、各 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;
    }
}
于 2009-06-27T06:48:47.510 に答える
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);
}
于 2009-06-27T07:07:44.383 に答える
0

すべての言語 (非常識な :D であるため JS を除く) では、switch ステートメントを定数式にする必要があります。

あなたは何をしようとしているのですか?SlotValue に getter を追加することで、SlotValue から int に簡単に移行できます。

于 2009-06-27T06:43:30.513 に答える
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;
}
于 2009-06-27T07:18:33.003 に答える
-2

あなたのプログラムでの SlotValue の目的はわかりませんが、それらの機能を十分に活用していないようです。列挙型インスタンスでメソッドを呼び出したり、それらの状態をクエリしたりできます。したがって、以下に示すように、列挙値のスイッチをメソッド呼び出し/状態クエリに変換します。

補足: この種の考え方 (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;
   }
}
于 2009-06-27T07:14:21.233 に答える