2009-02-10 2 views
85

Можно ли использовать == на перечислениях на Java, или мне нужно использовать .equals()? В моем тестировании всегда работает ==, но я не уверен, гарантирован ли я этим. В частности, на перечислении нет метода .clone(), поэтому я не знаю, можно ли получить перечисление, для которого .equals() будет возвращать другое значение, чем ==.Можно ли использовать == на enums в Java?

Например, это нормально:

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 
} 
+4

возможно дубликат [Сравнение членов перечислений Java: == или равно()] (http://stackoverflow.com/questions/1750435/comparing-java-enum-members -или-equals) – assylias

+0

@assylias этот вопрос пришел первым. Возможно, флаг для внимания, так как я не уверен, что эти два должны быть объединены. –

+0

@MattBall Я думаю, что ответ на ваш вопрос, который цитирует JLS, является лучшим ответом, поэтому я решил закрыть этот. – assylias

ответ

117

Просто мои 2 цента: Вот код для 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; 
    } 


} 
+1

Спасибо! Думаю, если бы я только подумал о том, чтобы войти в .equals() с компилятором, я бы увидел это ... – Kip

70

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

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

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

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

EDIT: спецификации не очень понятно, но section 8.9 состояния:

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

+0

Я хотел бы заверить ваше слово, но если бы вы могли предоставить ссылку на какую-то официальную документацию, которая была бы лучше ... – Kip

+0

Посмотрим на соответствующий бит спецификаций или документов Java. –

+0

Всё нормально? Это своего рода вопрос! Переключатель –

10

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

Ссылка:

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

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

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

dont be evil! Кстати, вы можете сделать аналогичную вещь со строками, которые интернированы ... – Chii

11

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

 
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() печатает имя, указанное в коде.
  • (Как упоминалось в другом сообщении) переменную типа перечисления можно сравнить с константами, используя структуру управления switch-case.
  • Всех значений в перечислении могут быть запрошены с помощью values поля, которое генерируемое 'для каждого типа перечислений
  • Вот большие сравнения один w.r.t удостоверения: значения перечислений выжить сериализации без клонирования.

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

 
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 делает все возможное, чтобы вы могли использовать идентификаторы значений перечисления для проверки равенства; это поощряемая практика.

+1

+1 Замечания по сериализации –

+1

Исправлена ​​ошибка сериализации. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id = 6277781 –

+0

@DavidI. Спасибо за обновление. Это очень тревожная ошибка, и это хорошо знать! –

3

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

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, поэтому я не могу этого сделать в моем случае. – Kip

1

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

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

+1

Это было http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781, которое теперь исправлено. –

1

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

Comparing Java enum members: == or equals()?

Смежные вопросы