Можно ли использовать == для перечислений в Java?

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

person Kip    schedule 10.02.2009    source источник
comment
возможный дубликат Сравнение членов перечисления Java: == или equals()?   -  person assylias    schedule 06.06.2012
comment
@assylias этот вопрос был первым. Возможно, отметьте ♦ вниманием, так как я не совсем уверен, следует ли их объединять.   -  person Matt Ball    schedule 03.09.2012
comment
@MattBall Я думаю, что ответ на ваш вопрос, в котором цитируется JLS, является лучшим ответом, поэтому я решил закрыть этот вопрос.   -  person assylias    schedule 03.09.2012


Ответы (8)


Просто мои 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;
    }


}
person Varkhan    schedule 10.02.2009
comment
Спасибо! Я думаю, если бы я только что подумал о том, чтобы перейти к .equals() с компилятором, я бы увидел это... - person Kip; 11.02.2009

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

Однако есть лучший способ написать метод round:

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 сообщает:

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

person Jon Skeet    schedule 10.02.2009
comment
Я хотел бы поверить вам на слово, но если бы вы могли дать ссылку на какую-нибудь официальную документацию, было бы лучше... - person Kip; 10.02.2009
comment
switch бесполезен, когда между разными случаями много совпадений. Кроме того, RoundingMode является частью java.math, поэтому я не могу добавить к нему метод. - person Kip; 10.02.2009
comment
О... и вы сомневаетесь в Джоне Ските? Вас тут давно не было ;) - person Joel Coehoorn; 10.02.2009
comment
перечисления в операторах переключения? Не знал, что это возможно. Я должен буду попробовать это однажды. - person luiscubal; 11.02.2009
comment
Инкапсуляция логики в перечисления с использованием абстрактных методов — это реальная сила перечислений. Это делает ваш код более надежным; когда вы добавите новое значение перечисления в будущем, компилятор заставит вас реализовать соответствующую логику, вам не нужно помнить о добавлении case к нескольким операторам switch. - person Andrew Swan; 11.02.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

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

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

person Dilum Ranatunga    schedule 11.02.2009
comment
исправлена ​​ошибка сериализации. bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781 - person David I.; 17.07.2013
comment
@ДэвидИ. Спасибо за обновления. Это очень неприятная ошибка, и полезно знать! - person Dilum Ranatunga; 19.07.2013
comment
@DilumRanatunga Сначала я думал, что это повлияет на меня, но, похоже, они работают нормально после передачи их через соединение RMI. - person David I.; 22.07.2013

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

Ссылка:

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

(в документации Sun ничего не нашел)

person levand    schedule 10.02.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));
}
person Peter Lawrey    schedule 10.02.2009
comment
@Peter, не могли бы вы включить импорт этого кода? Не удалось найти Unsafe.class. - person rumman0786; 15.04.2018
comment
@ rumman0786 jaxenter. com/ проверьте эту ссылку - person Dave Ankin; 02.10.2020

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

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);
}
person paulmurray    schedule 11.02.2009
comment
Да, но у меня нет java.math.RoundingMode, поэтому в моем случае я не могу этого сделать. - person Kip; 11.02.2009

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

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

person Community    schedule 08.09.2009
comment
Это было bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781, который теперь исправлен. - person Wilfred Hughes; 21.05.2012

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

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

person Tobias    schedule 14.02.2013