сравнивать - наследование enum java




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

Я знаю, что перечисления 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)
    {
        ...
    }
    ...
}

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


ТЛ; др

Другим вариантом является Objects.equals утилиты Objects.equals .

Objects.equals( thisEnum , thatEnum )

Objects.equals

равен operator == вместо .equals ()

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

Третьим вариантом является метод static Objects.equals найденный в классе утилиты Objects добавленном в Java 7 и более поздних версиях.

пример

Вот пример использования перечисления Month .

boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ;  // Returns `false`.

Выгоды

Я нахожу пару преимуществ для этого метода:

  • Null-безопасности
    • И null ➙ true
    • Либо null ➙ false
    • Отсутствие риска выброса NullPointerException
  • Компактный, читаемый

Как это устроено

Какова логика, используемая Objects.equals ?

Посмотрите сами, из исходного кода Java 10 OpenJDK :

return (a == b) || (a != null && a.equals(b));

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

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

JLS 8.9 Enums

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

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

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

Эта гарантия достаточно сильна, что Джош Блох рекомендует, чтобы, если вы настаиваете на использовании шаблона singleton, наилучшим способом его реализации является использование enum с одним элементом (см. Эффективное Java 2nd Edition, пункт 3: Принудительное использование свойства singleton с частный конструктор или тип перечисления, а также безопасность потоков в 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 :

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

В случае перечисления оба правильные и правильные !!


Вот приблизительный анализ времени для сравнения двух:

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

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

Я провел этот тест несколько раз с обоих IFs по одному за раз. «==» всегда немного быстрее.


Использование чего-либо другого, кроме == для сравнения констант enum, является бессмыслицей. Это похоже на сравнение объектов class с equals - не делайте этого!

Однако в Sun JDK 6u10 и ранее была неприятная ошибка (BugId 6277781 ), которая может быть интересна по историческим причинам. Эта ошибка не позволяла правильно использовать == на десериализованных перечислениях, хотя это, возможно, несколько угловой случай.


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

Тем не мение:

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

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

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


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

Я использую == , однако, поскольку это будет нулевой безопасностью.


Перечисления - это классы, которые возвращают один экземпляр (например, одиночные) для каждой константы перечисления, объявленной public static final field (неизменяемым), так что оператор == может использоваться для проверки их равенства, а не для использования метода equals()


Я предпочитаю использовать == вместо equals :

Другая причина, помимо других, уже обсуждавшихся здесь, заключается в том, что вы можете ввести ошибку, не осознавая ее. Предположим, что у вас есть это перечисление, которое точно такое же, но в отдельных pacakges (это не часто, но это может произойти):

Первое перечисление :

package first.pckg

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

Второе перечисление:

package second.pckg

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

Затем предположим, что вы используете равные, как в item.category который является first.pckg.Category но вы импортируете второе перечисление ( second.pckg.Category ) вместо первого, не осознавая этого:

import second.pckg.Category;
...

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

Таким образом, вы получите allways false из-за другого перечисления, хотя вы ожидаете true, потому что item.getCategory() - JAZZ . И это может быть немного сложно понять.

Итак, если вы вместо этого используете оператор == вас будет ошибка компиляции:

оператор == не может применяться к «second.pckg.Category», «first.pckg.Category»

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

Я хотел бы явно выделить эту конкретную разницу между оператором == и equals() :

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

Оператор == проверяет, ссылается ли эта ссылочная переменная (ы) на (-ы) на тот же объект .

Прикладной класс должен выполнять эту дифференциацию по мере необходимости.

В противном случае поведение по умолчанию будет соответствовать классу Object (на Java), где, как описано в http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals(java.lang.Object) :

Метод equals для класса Object реализует наиболее различающееся возможное отношение эквивалентности для объектов; то есть для любых непустых опорных значений x и y этот метод возвращает true тогда и только тогда, когда x и y относятся к одному и тому же объекту ( x == y имеет значение true ).





enums