Можно ли использовать == для перечислений в Java? - PullRequest
105 голосов
/ 10 февраля 2009

Можно ли использовать == для перечислений в 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
}

Ответы [ 8 ]

142 голосов
/ 10 февраля 2009

Только мои 2 цента: вот код для Enum.java, опубликованный Sun, и часть 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;
    }


}
76 голосов
/ 10 февраля 2009

Да, == хорошо - гарантированно будет только одна ссылка для каждого значения.

Тем не менее, есть лучший способ написать ваш метод раунда:

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

Даже лучший способ сделать это - поместить функциональность в само перечисление, чтобы вы могли просто позвонить roundingMode.round(someValue). Это доходит до сути перечислений Java - они объектно-ориентированные перечисления , в отличие от "именованных значений", найденных в других местах.

РЕДАКТИРОВАТЬ: спецификация не очень ясна, но раздел 8.9 состояния:

Тело типа enum может содержать константы перечисления. Константа перечисления определяет экземпляр типа enum. Тип enum не имеет других экземпляров чем те, которые определены его перечислением константы.

13 голосов
/ 11 февраля 2009

Да, вы создали отдельные экземпляры для каждого значения в перечислении:

public abstract class RoundingMode {
  public static final RoundingMode HALF_UP = new RoundingMode();
  public static final RoundingMode HALF_EVEN = new RoundingMode();

  private RoundingMode() {
    // private scope prevents any subtypes outside of this class
  }
}

Однако , конструкция enum дает вам различные преимущества:

  • toString () каждого экземпляра печатает имя, указанное в коде.
  • (Как упоминалось в другом посте) переменную типа enum можно сравнить с константами, используя управляющую структуру switch-case.
  • Все значения в перечислении можно запросить, используя поле values, которое «генерируется» для каждого типа перечисления
  • Вот большое сравнение с идентичностью: значения перечисления переживают сериализацию без клонирования.

Сериализация - это большая ошибка. Если бы я использовал приведенный выше код вместо перечисления, вот как будет вести себя равенство идентичностей:

RoundingMode original = RoundingMode.HALF_UP;
assert (RoundingMode.HALF_UP == original); // passes

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

assert (RoundingMode.HALF_UP == deserialized); // fails
assert (RoundingMode.HALF_EVEN == deserialized); // fails

Вы можете решить эту проблему без enum, используя технику, которая включает writeReplace и readResolve, (см. http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html)...

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

10 голосов
/ 10 февраля 2009

== сравнивает ссылки двух объектов. Для перечислений гарантируется, что будет только один экземпляр, и поэтому для любых двух одинаковых перечислений == будет истинным.

Справка:

http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

(ничего не смог найти в документах Sun)

6 голосов
/ 10 февраля 2009

Вот некоторый злой код, который может показаться вам интересным. : 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));
}
3 голосов
/ 11 февраля 2009

Перечисления - отличное место для застревания полиморфного кода.

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 голос
/ 14 февраля 2013

==, как правило, хорошо, и есть преимущества как ==, так и .equals(). Я лично предпочитаю всегда использовать .equals() при сравнении объектов, включая enum с. Смотрите также это обсуждение:

Сравнение членов перечисления Java: == or equals ()?

1 голос
/ 08 сентября 2009

Обратите внимание, что существует проблема при передаче enum через RMI / IIOP. Смотрите эту тему:

http://www.velocityreviews.com/forums/t390342-enum-equality.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...