22

Сравнение членов enum в Java: использовать == или equals()?

39

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

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

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

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

Какой из этих операторов правильнее использовать?

4 ответ(ов)

20

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

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

0

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

Тем не менее:

  1. Факт, что == никогда не вызывает NPE (NullPointerException), на мой взгляд, является недостатком ==. Строго говоря, в enum значениях почти никогда не должно быть null, так как любое состояние, которое нужно выразить через null, можно добавить в enum как дополнительный экземпляр. Если значение неожиданно оказывается null, я предпочел бы получить NPE, чем чтобы == беззвучно возвратило false. Поэтому я не согласен с мнением о том, что это «безопаснее во время выполнения»; лучше выработать привычку никогда не допускать, чтобы значения enum были @Nullable.

  2. Утверждение, что == быстрее, также не имеет смысла. В большинстве случаев вы будете вызывать .equals() на переменной, чей компиляционный тип — это класс enum, и в таких случаях компилятор может знать, что это то же самое, что и == (поскольку метод equals() у enum не может быть переопределен) и может оптимизировать этот вызов функции. Я не уверен, делает ли компилятор это в настоящее время, но если нет, и это приводит к проблемам с производительностью в Java в целом, то мне бы хотелось исправить компилятор, чем заставлять 100,000 Java-разработчиков менять свой стиль программирования под особенности производительности конкретной версии компилятора.

  3. Enum — это объекты. Для всех остальных типов объектов стандартным способом сравнения является .equals(), а не ==. Я считаю опасным делать исключение для enum, так как это делает код хрупким: если в какой-то день класс enum будет рефакторингом преобразован в нечто другое, то:

Метод сравнения Изменение enum → не-enum класс Изменение enum → примитивный тип
.equals() все работает ломается на этапе компиляции (вы это заметите)
== ломается без предупреждения (по-прежнему компилируется, но некорректно возвращает false) все работает

Из-за этой асимметрии (в правом верхнем и левом нижнем углу таблицы) в условиях неизбежных изменений кода аргумент о том, что это работает, также неверен: использование .equals() намного легче идентифицировать как правильное и оно безопаснее к будущим рефакторингам.

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

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

0

Одно из правил Sonar гласит, что значения перечислений (enum) следует сравнивать с помощью "==". Причины этого следующие:

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

  • обеспечивает такое же ожидаемое сравнение (содержимого), как и equals()
  • более безопасно в отношении значения null, чем equals()
  • предоставляет проверку на этапе компиляции (статическую), а не во время выполнения

По этим причинам предпочтение следует отдавать использованию ==, а не equals().

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

0

Вот пример простого теста времени, чтобы сравнить два способа сравнения:

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

}

Закомментируйте один из условий if за раз. Вот два способа сравнения из приведенного выше кода в дизассемблированном байт-коде:

 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

Первый метод (с equals) выполняет виртуальный вызов и проверяет возвращаемое булево значение из стека. Во втором случае (==) сравниваются адреса объектов напрямую из стека. В первом случае происходит больше операций.

Я провел этот тест несколько раз, каждый раз с одним из условий if. Метод == немного быстрее.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь