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


Я знаю, что перечисления Java скомпилированы для классов с частными конструкторами и кучей публичных статических членов. При сравнении двух членов данного перечисления я всегда использовал .equals(), например.

public useEnums(SomeEnum a)
{
    if(a.equals(SomeEnum.SOME_ENUM_VALUE))
    {
        ...
    }
    ...
}

Тем не менее, я просто натолкнулся на код, который использует оператор equals ==вместо .equals ():

public useEnums2(SomeEnum a)
{
    if(a == SomeEnum.SOME_ENUM_VALUE)
    {
        ...
    }
    ...
}

Какой оператор я должен использовать?


1397


источник


Ответы:


Оба являются технически правильными. Если вы посмотрите на исходный код для .equals(), он просто ==,

я использую ==, однако, поскольку это будет нецелесообразным.


1250



Можно ==использоваться на enum?

Да: перечисления имеют жесткий контроль над экземплярами, который позволяет использовать ==для сравнения экземпляров. Вот гарантия, предоставляемая спецификацией языка (выделение мной):

JLS 8.9 Enums

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

Ошибка компиляции, чтобы попытаться явно создать экземпляр типа перечисления. final cloneметода в Enumгарантирует, что enumконстанты никогда не могут быть клонированы, а специальное обращение с помощью механизма сериализации гарантирует, что дублирующие экземпляры никогда не создаются в результате десериализации. Отражающее создание типов перечислений запрещено. Вместе эти четыре вещи гарантируют, что никакие случаи enumтипа существуют вне тех, которые определены enumконстанты.

Потому что есть только один экземпляр каждого enumпостоянная, допустимо использовать ==оператора вместо equalsпри сравнении двух ссылок на объекты, если известно, что по меньшей мере один из них относится к enumпостоянная , (The equalsметода в Enumэто finalметод, который просто вызывает super.equalsпо его аргументу и возвращает результат, таким образом выполняя сравнение идентичности.)

Эта гарантия достаточно сильна, что Джош Блох рекомендует, чтобы, если вы настаиваете на использовании шаблона singleton, лучший способ реализовать его - использовать одноэлементный enum(видеть: Эффективное Java 2nd Edition, пункт 3: Принудительное использование свойства singleton с помощью частного конструктора или типа перечисления ; также Безопасность потоков в Синглтоне )


Каковы различия между ==а также equals?

Напомним, что нужно сказать, что в целом, ==НЕ является жизнеспособной альтернативой equals, Однако, когда это возможно (например, с enum), есть два важных различия:

==никогда не бросает NullPointerException

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

==подлежит проверке совместимости типов во время компиляции

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

Должен ==использовать, когда это применимо?

Блох специально упоминает, что неизменные классы, которые имеют надлежащий контроль над своими экземплярами, могут гарантировать своим клиентам, что ==можно использовать. enumконкретно упоминается для иллюстрации.

Пункт 1: Рассмотрите статические заводские методы вместо конструкторов

[...] он позволяет неизменному классу гарантировать, что нет двух равных экземпляров: a.equals(b)если и только если a==b, Если класс делает эту гарантию, то ее клиенты могут использовать ==оператора вместо equals(Object)метод, который может привести к повышению производительности. Типы перечислений предоставляют эту гарантию.

Резюмируя, аргументы для использования ==на enumнаходятся:

  • Оно работает.
  • Это быстрее.
  • Это безопаснее во время выполнения.
  • Это безопаснее во время компиляции.

953



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

С другой стороны, на самом деле нет необходимости использовать ==написать нулевой безопасный код, если вы напишете свой equals()как это:

public useEnums(SomeEnum a)
{
    if(SomeEnum.SOME_ENUM_VALUE.equals(a))
    {
        ...
    }
    ...
}

Это лучшая практика, известная как Сравнить константы слева что вы определенно должны следовать.


69



Как говорили другие, оба ==а также .equals()работают в большинстве случаев. Точность времени компиляции, что вы не сравниваете совершенно разные типы объектов, которые указали другие, действительна и полезна, однако конкретный тип ошибок сравнения объектов двух разных типов времени компиляции также будет найден FindBugs (и, вероятно, Eclipse / IntelliJ), поэтому компилятор Java, обнаруживающий это, не добавляет дополнительной безопасности.

Однако:

  1. Дело в том, что ==никогда не бросает NPE в мой разум недостаток из ==, Вряд ли когда-либо возникнет необходимость в enumтипы, которые должны быть null, поскольку любое дополнительное состояние, которое вы можете выразить через nullможно просто добавить в enumкак дополнительный экземпляр. Если это неожиданно null, Я бы предпочел иметь NPE, чем ==молча оценивая ложь. Поэтому я не согласен с это безопаснее во время выполнения мнение; лучше привыкнуть никогда не позволять enumзначения @Nullable,
  2. Довод о том, что ==является Быстрее также является фиктивным. В большинстве случаев вы будете звонить .equals()на переменной, чей тип времени компиляции является классом enum, и в этих случаях компилятор может знать, что это то же самое, что и ==(поскольку enum«s equals()метод не может быть переопределен) и может оптимизировать вызов функции. Я не уверен, что компилятор в данный момент это делает, но если это не так, и в целом это проблема производительности в Java, я бы предпочел исправить компилятор, чем 100 000 программистов на Java изменили свой стиль программирования в соответствии с характеристики производительности конкретного компилятора.
  3. enumsявляются объектами. Для всех других типов объектов стандартное сравнение .equals(), а не ==, Я думаю, что опасно делать исключение для enumsпотому что вы можете случайно сравнить объекты с ==вместо equals(), особенно если вы реорганизуете enumв класс non-enum. В случае такого рефакторинга Оно работает точка сверху неверна. Убедиться, что использование ==правильно, вам нужно проверить, является ли значение, о котором идет речь, либо enumили примитив; если бы это было не- enumкласс, было бы неправильно, но легко пропустить, потому что код все равно будет компилироваться. Единственный случай, когда использование .equals()было бы неправильно, если бы эти ценности были примитивными; в этом случае код не будет компилироваться, поэтому его гораздо сложнее пропустить. Следовательно, .equals()намного легче идентифицировать как правильное и безопаснее против будущих рефакторингов.

Я на самом деле думаю, что язык Java должен был определить == на объектах для вызова .equals () в левом значении и ввести отдельный оператор для идентификации объекта, но это не так, как была определена Java.

Таким образом, я все же считаю, что аргументы в пользу использования .equals()для enumтипы.


56



Here is a crude timing test to compare the two:

import java.util.Date;

public class EnumCompareSpeedTest {

    static enum TestEnum {ONE, TWO, THREE }

    public static void main(String [] args) {

        Date before = new Date();
        int c = 0;

        for(int y=0;y<5;++y) {
            for(int x=0;x<Integer.MAX_VALUE;++x) {
                if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
                if(TestEnum.ONE == TestEnum.TWO){++c;}              
            }
        }

        System.out.println(new Date().getTime() - before.getTime());
    }   

}

Comment out the IFs one at a time. Here are the two compares from above in disassembled byte-code:

 21  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 24  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 27  invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
 30  ifeq 36

 36  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 39  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 42  if_acmpne 48

The first (equals) performs a virtual call and tests the return boolean from the stack. The second (==) compares the object addresses directly from the stack. In the first case there is more activity.

I ran this test several times with both IFs one at a time. The "==" is ever so slightly faster.


12



In case of enum both are correct and right!!


11



I prefer to use == instead of equals:

Other reason, in addition to the others already discussed here, is you could introduce a bug without realizing it. Suppose you have this enums which is exactly the same but in separated pacakges (it's not common, but it could happen):

First enum:

package first.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Second enum:

package second.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Then suppose you use the equals like next in item.category which is first.pckg.Category but you import the second enum (second.pckg.Category) instead the first without realizing it:

import second.pckg.Category;
...

Category.JAZZ.equals(item.getCategory())

So you will get allways false due is a different enum although you expect true because item.getCategory() is JAZZ. And it could be be a bit difficult to see.

So, if you instead use the operator == you will have a compilation error:

operator == cannot be applied to "second.pckg.Category", "first.pckg.Category"

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

10



Using anything other than == to compare enum constants is nonsense. It's like comparing class objects with equals – don't do it!

However, there was a nasty bug (BugId 6277781) in Sun JDK 6u10 and earlier that might be interesting for historical reasons. This bug prevented proper use of == on deserialized enums, although this is arguably somewhat of a corner case.


4



Enums are classes that return one instance (like singletons) for each enumeration constant declared by public static final field (immutable) so that == operator could be used to check their equality rather than using equals() method


4



In short, both have pros and cons.

On one hand, it has advantages to use ==, as described in the other answers.

On the other hand, if you for any reason replace the enums with a different approach (normal class instances), having used == bites you. (BTDT.)


1



I want to complement polygenelubricants answer:

I personally prefer equals(). But it lake the type compatibility check. Which I think is an important limitation.

To have type compatibility check at compilation time, declare and use a custom function in your enum.

public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable

With this, you got all the advantage of both solution: NPE protection, easy to read code and type compatibility check at compilation time.

I also recommend to add an UNDEFINED value for enum.


0