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

Wiem, że enumery Javy są kompilowane do klas z prywatnymi konstruktorami i grupą publicznych statycznych członków. Porównując dwa człony danego enum, zawsze używałem .equals(), np.

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

Natknąłem się jednak na jakiś kod, który zamiast tego używa operatora równości ==.equals ():

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

Jakiego operatora powinienem używać?

 1452
Author: GURMEET SINGH, 2009-11-17

14 answers

Oba są poprawne technicznie. Jeśli spojrzysz na kod źródłowy .equals(), po prostu przesuwa się do ==.

Używam ==, jednak, ponieważ będzie to null bezpieczne.

 1310
Author: Reverend Gonzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-10 15:55:55

Czy można użyć == na enum?

Tak: enums mają ścisłe kontrolki instancji, które pozwalają używać == do porównywania instancji. Oto gwarancja zapewniona przez specyfikację języka (podkreślona przeze mnie):

JLS 8.9 Enums

Typ enum nie ma instancji innych niż te zdefiniowane przez jego stałe enum.

Próba jawnego utworzenia instancji typu enum jest błędem w czasie kompilacji. Metoda final clone w Enum zapewnia, że enum stałe nigdy nie mogą być klonowane, a specjalne traktowanie przez mechanizm serializacji zapewnia, że duplikaty instancji nigdy nie są tworzone w wyniku deserializacji. Zabronione jest tworzenie refleksyjnych instancji typów enum. Razem te cztery rzeczy zapewniają, że żadne instancje typu enum nie istnieją poza tymi zdefiniowanymi przez stałe enum.

Ponieważ istnieje tylko jedna instancja każdej enum stałej, dopuszczalne jest użycie operatora == zamiast equals metoda porównująca dwa odniesienia do obiektów, jeśli wiadomo, że przynajmniej jedno z nich odnosi się do enum stałej. (Metoda equals W Enum jest metodą final, która po prostu wywołuje super.equals na swoim argumencie i zwraca wynik, wykonując w ten sposób porównanie tożsamości.)

Ta gwarancja jest na tyle silna, że Josh Bloch zaleca, że jeśli nalegasz na użycie wzoru Singletona, najlepszym sposobem jego wdrożenia jest użycie jednoelementowego enum (Zobacz: {55]}skutecznego Java 2nd Edition, Pozycja 3: egzekwowanie własności singleton za pomocą prywatnego konstruktora lub typu enum; także bezpieczeństwo wątków w Singleton )


Jakie są różnice między == i equals?

Dla przypomnienia, należy powiedzieć, że ogólnie == nie jest realną alternatywą dla equals. Kiedy jednak tak jest (np. z enum), należy wziąć pod uwagę dwie ważne różnice:

== nigdy nie rzucaj NullPointerException

enum Color { BLACK, WHITE };

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

== jest test zgodności typu w czasie kompilacji

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!

Czy {[2] } należy stosować, gdy ma zastosowanie?

Bloch wyraźnie wspomina, że niezmienne klasy, które mają właściwą kontrolę nad swoimi instancjami, mogą zagwarantować swoim klientom, że == jest użyteczna. enum jest specjalnie wspomniany dla przykładu.

Item 1: rozważ statyczne metody fabryczne zamiast konstruktorów

[...] pozwala klasie niezmiennej złożyć gwarancję że nie istnieją dwie równe instancje: a.equals(b) wtedy i tylko wtedy, gdy a==b. Jeśli klasa składa taką gwarancję, to jej klienci mogą użyć operatora == zamiast metody equals(Object), co może skutkować lepszą wydajnością. Typy Enum zapewniają tę gwarancję.

Podsumowując, argumenty za użyciem == na enum są następujące:

    To działa. Jest szybszy. Jest bezpieczniej w czasie ucieczki.
  • to bezpieczniejsze w czasie kompilacji.
 980
Author: polygenelubricants,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-07 09:06:45

Użycie == do porównania dwóch wartości enum działa, ponieważ dla każdej stałej enum jest tylko jeden obiekt.

Na marginesie, w rzeczywistości nie ma potrzeby używania == do napisania bezpiecznego kodu null, jeśli napiszesz swój equals() w następujący sposób:

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

Jest to najlepsza praktyka znana jako Porównaj Stałe Z Lewej , którą zdecydowanie powinieneś przestrzegać.

 72
Author: Pascal Thivent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 17:53:22

Jak mówili inni, zarówno == jak i .equals() działają w większości przypadków. Pewność w czasie kompilacji, że nie porównujesz zupełnie różnych typów obiektów, które inni wskazali, jest poprawna i korzystna, jednak szczególny rodzaj błędu porównywania obiektów dwóch różnych typów kompilacji może być również znaleziony przez FindBugs (i prawdopodobnie przez Eclipse/IntelliJ compile time inspections), więc znalezienie kompilatora Javy nie dodaje zbyt wiele dodatkowych informacji bezpieczeństwo.

Jednakże:

  1. fakt, że == nigdy nie rzuca NPE w mój umysł jestwadą ==. Nie powinno być potrzeby, aby typy enum były null, ponieważ każdy dodatkowy stan, który chcesz wyrazić za pomocą null, można po prostu dodać do enum jako dodatkową instancję. Jeśli jest nieoczekiwanie null, wolałbym mieć NPE niż == po cichu Oceniać na false. Dlatego nie zgadzam się z opinią jest bezpieczniej w czasie trwania; jest lepiej aby wejść w nawyk nigdy nie pozwalać enum wartościom @Nullable.
  2. argument, że == jest szybszy jest również fałszywy. W większości przypadków wywołujesz .equals() na zmiennej, której typem czasu kompilacji jest klasa enum, a w tych przypadkach kompilator może wiedzieć, że jest to to samo co == (ponieważ metoda enum s equals() nie może zostać nadpisana) i może zoptymalizować wywołanie funkcji. Nie jestem pewien, czy kompilator obecnie to robi, ale jeśli tak nie jest, a okazuje się, że jest to wydajność problem w Javie ogólnie rzecz biorąc, to wolałbym naprawić kompilator, niż mieć 100,000 programistów Java zmienić swój styl programowania, aby dopasować się do charakterystyki wydajności konkretnej wersji kompilatora.
  3. enums są obiektami. Dla wszystkich innych typów obiektów standardowym porównaniem jest .equals(), a nie ==. Myślę, że niebezpiecznie jest zrobić wyjątek dla enums, ponieważ może skończyć się przypadkowym porównaniem obiektów z == zamiast equals(), szczególnie jeśli refaktorujesz enum do klasy nie-enum. W przypadku takiego refaktoryzacji punkt Działa z góry jest błędny. Aby przekonać siebie, że użycie {[0] } jest poprawne, musisz sprawdzić, czy dana wartość jest albo enum, albo prymitywną; jeśli byłaby to klasa nie-enum, byłoby to błędne, ale łatwe do przeoczenia, ponieważ kod wciąż byłby kompilowany. Jedyny przypadek, w którym użycie .equals() byłoby błędne, to gdyby Dane wartości były prymitywne; w takim przypadku kod nie kompilowałby się, więc trudniej go przeoczyć. Stąd {[1] } jest dużo łatwiejsze do zidentyfikowania jako poprawne i jest bezpieczniejsze przed przyszłymi refaktoringami.

Myślę, że język Java powinien mieć zdefiniowane = = na obiektach do wywołania .equals () po lewej stronie wartości i wprowadzić oddzielny operator dla tożsamości obiektu, ale nie tak została zdefiniowana Java.

Podsumowując, nadal uważam, że argumenty przemawiają za używaniem .equals() dla typów enum.

 56
Author: Tobias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 03:43:54

Oto prymitywny test czasu, aby porównać te dwa:

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

}

Komentuj IFs pojedynczo. Oto dwa porównania z góry w demontowanym kodzie bajtowym:

 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

First (equals) wykonuje wirtualne wywołanie i testuje wartość logiczną zwracaną ze stosu. Drugi ( = = ) porównuje adresy obiektów bezpośrednio ze stosu. W pierwszym przypadku jest więcej aktywności.

Przeprowadziłem ten test kilka razy z obu IFs po kolei. "=="Jest zawsze tak lekko szybciej.

 12
Author: ChrisCantrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-14 21:19:26

Wolę używać == zamiast equals:

Innym powodem, oprócz innych już omówionych tutaj, jest to, że możesz wprowadzić błąd nie zdając sobie z tego sprawy. Załóżmy, że masz te liczby, które są dokładnie takie same, ale w oddzielonych pacakges (nie jest to powszechne, ale może się zdarzyć): {]}

Pierwszy enum :

package first.pckg

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

Drugie enum:

package second.pckg

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

Wtedy Załóżmy, że używasz równości jak next w item.category czyli {[7] } ale importujesz drugie enum (second.pckg.Category) zamiast pierwszego nie zdając sobie z tego sprawy:

import second.pckg.Category;
...

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

Więc dostaniesz allways false due jest innym enum, chociaż oczekujesz prawdy, ponieważ item.getCategory() jest JAZZ. I to może być trochę trudne do zobaczenia.

Jeśli zamiast tego użyjesz operatora ==, pojawi się błąd kompilacji:

Operator = = nie może być zastosowany do " second.pckg.Kategoria", " pierwsza.pckg.Kategoria "

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 
 11
Author: Pau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-21 07:31:45

W przypadku enum oba są poprawne i poprawne!!

 10
Author: Suraj Chandran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-23 13:03:10

Używanie czegokolwiek innego niż == do porównywania stałych enum jest nonsensem. To jak porównywanie class obiektów z equals – nie rób tego!

Jednak pojawił się paskudny błąd (BugId 6277781) W Sun JDK 6u10 i wcześniej może to być interesujące ze względów historycznych. Ten błąd uniemożliwił prawidłowe użycie == Na deserializowanych enumach, chociaż prawdopodobnie jest to przypadek narożny.

 4
Author: Tomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-26 20:40:01

Enums są klasami, które zwracają jedną instancję (jak singletony) dla każdej stałej wyliczenia zadeklarowanej przez public static final field (immutable) tak, że operator == może być użyty do sprawdzenia ich równości zamiast metody equals()

 4
Author: ΦXocę 웃 Пepeúpa ツ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-07 15:21:13

Krótko mówiąc, oba mają plusy i minusy.

Z jednej strony ma zalety używania ==, jak opisano w innych odpowiedziach.

Z drugiej strony, jeśli z jakiegokolwiek powodu zastąpisz enums innym podejściem (normalne instancje klas), po użyciu ==. (BTDT.)

 1
Author: glglgl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-05 20:05:39

Powodem, dla którego enums działa łatwo z = = jest to, że każda zdefiniowana instancja jest również singletonem. Więc porównanie tożsamości za pomocą = = zawsze będzie działać.

Ale używanie = = ponieważ działa z enums oznacza, że cały Twój kod jest ściśle powiązany z używaniem tego enum.

Na przykład: Enums może zaimplementować interfejs. Załóżmy, że obecnie używasz enum, które implementuje interfejs 1. Jeśli później ktoś ją zmieni lub wprowadzi nową klasę Impl1 jako implementację tego samego interfejsu. Następnie, jeśli zaczniesz używać instancji Impl1, będziesz miał dużo kodu do zmiany i przetestowania z powodu wcześniejszego użycia ==.

Dlatego najlepiej stosować się do tego, co uważa się za dobrą praktykę, chyba że istnieje uzasadniony zysk.

 1
Author: Dev Amitabh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-12 03:19:17

Chcę uzupełnić polygenelubricants odpowiedź:

Ja osobiście wolę equals (). Ale sprawdzanie zgodności typu. Co moim zdaniem jest ważnym ograniczeniem.

Aby sprawdzić zgodność typów w czasie kompilacji, zadeklaruj i użyj niestandardowej funkcji w swoim enum.

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

Dzięki temu masz wszystkie zalety obu rozwiązań: ochronę NPE, łatwy do odczytania kod i sprawdzenie zgodności typu w czasie kompilacji.

Polecam również dodać UNDEFINED wartość dla enum.

 0
Author: Christ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-17 08:43:54

Enum w środku jest zbiorem stałych liczb całkowitych. "=="jest tak samo poprawne i poprawne, jak gdybyś porównał dwie liczby całkowite.

 -3
Author: Kamil Tomasz Jarmusik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-15 21:24:42

Chciałbym wyraźnie podkreślić tę specyficzną różnicę między operatorem == i metodą equals():

Metoda equals() ma na celu sprawdzenie, czy zawartość obiektu(obiektów), do których odnoszą się zmienne odniesienia, jest taka sama.

Operator == sprawdza, czy odnośne zmienne odnoszą się do tego samego obiektu.

To do klasy implementującej należy zapewnienie tego zróżnicowania w zależności od potrzeb podanie.

W przeciwnym razie domyślne zachowanie będzie dostarczane przez klasę Object (w Javie), gdzie jak wyjaśniono w http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals(java.lang.Object):

Metoda equals dla klasy Object implementuje najbardziej rozróżniającą możliwą relację równoważności na obiektach; to znaczy dla dowolnych wartości odniesienia innych niż null x i y, metoda ta zwraca true wtedy i tylko wtedy, gdy x i y odnoszą się do ten sam obiekt (x == y ma wartość true).

 -4
Author: sangv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-19 22:51:21