Javaの列挙型で==を使用しても問題ありませんか?


111

==Javaの列挙型で使用しても大丈夫ですか、それとも使用する必要があります.equals()か?私のテストでは==常に動作しますが、それが保証されているかどうかはわかりません。特に、.clone()列挙型にはメソッドがないため、とは.equals()異なる値を返す列挙型を取得できるかどうかはわかりません==

たとえば、これは問題ありませんか?

public int round(RoundingMode roundingMode) {
  if(roundingMode == RoundingMode.HALF_UP) {
    //do something
  } else if (roundingMode == RoundingMode.HALF_EVEN) {
    //do something
  }
  //etc
}

または、次のように記述する必要がありますか?

public int round(RoundingMode roundingMode) {
  if(roundingMode.equals(RoundingMode.HALF_UP)) {
    //do something
  } else if (roundingMode.equals(RoundingMode.HALF_EVEN)) {
    //do something
  }
  //etc
}


@assyliasこの質問が最初に来ました。おそらく♦注意のフラグです。2つをマージする必要があるかどうかは本当にわからないからです。
Matt Ball

@MattBall JLSを引用する質問への回答が最良の回答だと思うので、この回答を閉じることにしました。
assylias 2012

回答:


149

ちょうど私の2セント:Sunによって公開されたEnum.javaのコードとJDKの一部を次に示します。

public abstract class Enum<E extends Enum<E>>
    implements Comparable<E>, Serializable {

    // [...]

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) { 
        return this==other;
    }


}

4
ありがとう!コンパイラーで.equals()にステップインするつもりだったとしたら、私はこれを見たことがあると思います...
Kip

77

はい、==で結構です-各値の参照が1つだけであることが保証されています。

ただし、ラウンドメソッドを記述するより良い方法があります。

public int round(RoundingMode roundingMode) {
  switch (roundingMode) {
    case HALF_UP:
       //do something
       break;
    case HALF_EVEN:
       //do something
       break;
    // etc
  }
}

でも、より良い、それを行う方法は、列挙型自体の中に機能を置くことですので、あなただけ呼び出すことができますroundingMode.round(someValue)。これは、Java enumの中心に到達します。他の場所にある「名前付き値」とは異なり、オブジェクト列挙型です。

編集:仕様はあまり明確ではありませんが、セクション8.9は次のように述べています:

enum型の本体には、enum定数を含めることができます。列挙定数は、列挙型のインスタンスを定義します。列挙型には、その列挙定数で定義されたもの以外のインスタンスはありません。


私はそれをあなたの言葉にしたいと思いますが、あなたがより良いだろういくつかの公式ドキュメントへのリンクを提供できれば...
Kip

異なるケース間で多くの重複がある場合、スイッチは役に立ちません。また、RoundingModeはjava.mathの一部であるため、メソッドを追加できません。
キップ

2
おお、そしてジョン・スキートを疑う?あなたはここにあまり長くいませんでした;)
Joel Coehoorn、2009

switchステートメントの列挙型?それが可能であるとは知りませんでした。いつか試してみます。
luiscubal

抽象メソッドを使用してenum内にロジックをカプセル化することは、enumの真の威力です。これにより、コードがはるかに堅牢になります。将来新しい列挙値を追加すると、コンパイラーは関連するロジックを実装するように強制します。複数のswitchステートメントにケースを追加することを覚えておく必要はありません。
アンドリュース

13

はい。これは、列挙型の各値に対してシングルトンインスタンスを作成した場合と同じです。

パブリック抽象クラスRoundingMode {
  public static final RoundingMode HALF_UP = new RoundingMode();
  public static final RoundingMode HALF_EVEN = new RoundingMode();

  プライベートRoundingMode(){
    //プライベートスコープは、このクラス外のサブタイプを防止します
  }
}

ただしenum構成にはさまざまな利点があります。

  • 各インスタンスのtoString()は、コードで指定された名前を出力します。
  • (別の投稿で述べたように)列挙型の変数は、switch-case制御構造を使用して定数と比較できます。
  • 列挙型のすべての値は、values列挙型ごとに「生成」されるフィールドを使用して照会できます
  • これがアイデンティティの比較に関する重要な1つです。列挙型の値は、クローンを作成せずにシリアル化しても存続します。

連載は大変です。列挙型ではなく上記のコードを使用した場合、IDの等価性は次のようになります。

RoundingMode original = RoundingMode.HALF_UP;
assert(RoundingMode.HALF_UP == original); //合格

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(original);
oos.flush();

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
RoundingMode deserialized =(RoundingMode)ois.readObject();

アサート(RoundingMode.HALF_UP ==逆シリアル化); //失敗する
アサート(RoundingMode.HALF_EVEN ==逆シリアル化); //失敗する

あなたはできますが含ま技術を使用して、列挙せずにこの問題に対処writeReplaceしてreadResolve、(参照http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.htmlを)...

ポイントは私が思うに-Javaは、等価性をテストするためにenum値のIDを使用できるようにするために、その道を離れます。これは推奨される方法です。


1
シリアライゼーションのバグが修正されました。bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781
デビッドI.

@DavidI。更新していただきありがとうございます。これは非常に厄介なバグであり、知っておくと役に立ちます。
Dilum Ranatunga 2013

1
@DilumRanatunga最初はこれが影響を受けると思っていましたが、RMI接続を介して渡した後は問題なく動作しているようです。
David I.


6

ここに、興味深いと思われるいくつかの邪悪なコードがあります。:D

public enum YesNo {YES, NO}

public static void main(String... args) throws Exception {
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    field.setAccessible(true);
    Unsafe unsafe = (Unsafe) field.get(null);
    YesNo yesNo = (YesNo) unsafe.allocateInstance(YesNo.class);

    Field name = Enum.class.getDeclaredField("name");
    name.setAccessible(true);
    name.set(yesNo, "YES");

    Field ordinal = Enum.class.getDeclaredField("ordinal");
    ordinal.setAccessible(true);
    ordinal.set(yesNo, 0);

    System.out.println("yesNo " + yesNo);
    System.out.println("YesNo.YES.name().equals(yesNo.name()) "+YesNo.YES.name().equals(yesNo.name()));
    System.out.println("YesNo.YES.ordinal() == yesNo.ordinal() "+(YesNo.YES.ordinal() == yesNo.ordinal()));
    System.out.println("YesNo.YES.equals(yesNo) "+YesNo.YES.equals(yesNo));
    System.out.println("YesNo.YES == yesNo " + (YesNo.YES == yesNo));
}

1
@ピーターは、このコードのインポートを含めることができますか?Coundが見つかりませんUnsafe.class
rumman0786

3

列挙型は、ポリモーフィックコードを妨害するのに最適な場所です。

enum Rounding {
  ROUND_UP {
    public int round(double n) { ...; }
  },
  ROUND_DOWN {
    public int round(double n) { ...; }
  };

  public abstract int round(double n);
}

int foo(Rounding roundMethod) {
  return roundMethod.round(someCalculation());
}

int bar() {
  return foo(Rounding.ROUND_UP);
}

1
はい、でも私はjava.math.RoundingModeを所有していないので、私の場合はこれを行うことができません。
キップ


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.