Wiem, że wyliczenia Java są kompilowane do klas z prywatnymi konstruktorami i grupą publicznych członków statycznych. Porównując dwóch członków danego wyliczenia, zawsze używałem .equals()
np
public useEnums(SomeEnum a)
{
if(a.equals(SomeEnum.SOME_ENUM_VALUE))
{
...
}
...
}
Jednak właśnie natknąłem się na kod, który używa operatora równości ==
zamiast .equals ():
public useEnums2(SomeEnum a)
{
if(a == SomeEnum.SOME_ENUM_VALUE)
{
...
}
...
}
Którego operatora powinienem używać?
Odpowiedzi:
Oba są technicznie poprawne. Jeśli spojrzysz na kod źródłowy
.equals()
, po prostu odracza się na==
.Używam
==
jednak, ponieważ będzie to bezpieczne.źródło
.equals()
jej wprowadzeniem? Wiem, że nie.Może
==
być używany naenum
?Tak: wyliczenia mają ścisłą kontrolę instancji, która umożliwia
==
porównywanie instancji. Oto gwarancja zawarta w specyfikacji języka (moje wyróżnienie):Ta gwarancja jest na tyle silna, że Josh Bloch zaleca, aby upierać się przy używaniu wzorca singletonu, najlepszym sposobem na jego wdrożenie jest użycie jednego elementu
enum
(patrz: Skuteczna Java 2. wydanie, pozycja 3: Egzekwowanie właściwości singletonu za pomocą prywatny konstruktor lub typ enum ; także bezpieczeństwo wątków w Singleton )Jakie są różnice między
==
iequals
?Dla przypomnienia należy powiedzieć, że ogólnie
==
NIE jest realną alternatywą dlaequals
. Gdy jednak tak jest (np. Zenum
), należy wziąć pod uwagę dwie ważne różnice:==
nigdy nie rzucaNullPointerException
==
podlega kontroli zgodności typu w czasie kompilacjiCzy
==
należy stosować, jeśli dotyczy?Bloch wspomina w szczególności, że niezmienne klasy, które mają odpowiednią kontrolę nad swoimi instancjami, mogą zagwarantować
==
użyteczność ich klientom .enum
jest konkretnie wspomniany jako przykład.Podsumowując, argumenty za korzystanie
==
naenum
to:źródło
Color nothing = null;
czy IMHO jest błędem i powinien zostać naprawiony, twoje wyliczenie ma dwie wartości, a nie trzy ( patrz komentarz Toma Hawtina) 4. W czasie kompilacji jest bezpieczniej : OK, aleequals
nadal by powróciłfalse
. Na koniec, ja tego nie kupuję, wolęequals()
dla czytelności (patrz komentarz Kevina).foo.equals(bar)
jest bardziej czytelny niżfoo == bar
Używanie
==
do porównywania dwóch wartości wyliczeniowych działa, ponieważ dla każdej stałej wyliczeniowej jest tylko jeden obiekt.Na marginesie, w rzeczywistości nie ma potrzeby
==
pisania kodu o wartości zerowej, jeśli piszesz cośequals()
takiego:Jest to najlepsza praktyka znana jako Porównaj stałe z lewej , którą zdecydowanie powinieneś stosować.
źródło
.equals()
wartości ciągów, ale nadal uważam, że jest to kiepski sposób na pisanie kodu o zerowej wartości. Wolałbym mieć operatora (lub metodę, ale wiem, że [obiekt zerowy] .equals nigdy nie będzie zerowo bezpieczny w Javie z powodu tego, jak działa operator kropki), który zawsze jest bezpieczny zerowo, niezależnie od kolejność, w jakiej jest używany. Semantycznie operator „równa się” powinien zawsze dojeżdżać do pracy.?.
), będę nadal korzystać z tej praktyki w porównaniu do stałych i, TBH, nie uważam jej za gównianą, może po prostu mniej „naturalną”.null
Enum jest zwykle błąd. Masz wyliczenie wszystkich możliwych wartości, a oto kolejna!@Nullable
, uważam, że porównywanie stałych z lewej strony jest antypatternem, ponieważ rozprzestrzenia on niejednoznaczność co do tego, które wartości mogą być zerowe. Typy wyliczeniowe prawie nigdy nie powinny być@Nullable
, więc wartość zerowa byłaby błędem programowania; w takim przypadku, im szybciej wyrzucisz NPE, tym bardziej prawdopodobne jest, że znajdziesz błąd programowy w miejscu, w którym pozwoliłeś mu być zerowy. Zatem „najlepsza praktyka ... którą zdecydowanie powinieneś zastosować” jest po prostu błędna, jeśli chodzi o typy wyliczeniowe.Jak mówili inni, zarówno
==
i.equals()
praca w większości przypadków. Pewność czasu kompilacji, że nie porównujesz całkowicie różnych typów obiektów, na które wskazali inni, jest ważna i korzystna, jednak szczególny rodzaj błędu porównywania obiektów dwóch różnych typów czasu kompilacji znalazłby również FindBugs (i prawdopodobnie Inspekcja czasu kompilacji Eclipse / IntelliJ), więc znalezienie kompilatora Java nie zapewnia tak dużego bezpieczeństwa.Jednak:
==
nie rzuca NPE w moim umyśle jest wadą z==
. Prawie nigdy nie powinna istnieć potrzeba istnieniaenum
typównull
, ponieważ każdy dodatkowy stan, który możesz chcieć wyrazić,null
może zostać dodany doenum
dodatkowej instancji. Jeśli jest to nieoczekiwanienull
, wolałbym mieć NPE niż==
dyskretną ocenę na fałsz. Dlatego nie zgadzam się z tym, że jest bezpieczniejsza w czasie wykonywania ; lepiej jest przyzwyczaić się do tego, aby nigdy nie pozwalać na to, byenum
wartości były@Nullable
.==
jest szybszy, jest również fałszywy. W większości przypadków będzie zadzwonić.equals()
na zmienną, której typ kompilacji czas jest klasa enum, aw tych przypadkach kompilator może wiedzieć, że to jest taki sam jak==
(ponieważenum
„sequals()
metoda nie może być zmienione) i można zoptymalizować wywołanie funkcji z dala. Nie jestem pewien, czy kompilator obecnie to robi, ale jeśli tak nie jest i okazuje się, że jest to ogólnie problem z wydajnością w Javie, wolę naprawić kompilator niż pozwolić 100 000 programistów Java zmienić swój styl programowania, aby pasował cechy wydajności konkretnej wersji kompilatora.enums
są obiektami. Dla wszystkich innych typów obiektów standardowe porównanie.equals()
nie jest==
. Wydaje mi się, że tworzenie wyjątków jest niebezpieczne,enums
ponieważ==
zamiast tegoequals()
możesz przypadkowo porównać obiekty , szczególnie jeślienum
przekształcisz an w klasę nie-enum. W przypadku takiego refaktoryzacji to działa punkt z góry jest błędny. Aby przekonać się, że użycie==
jest poprawne, musisz sprawdzić, czy dana wartość jest alboenum
prymitywna; gdyby nie była toenum
klasa, byłoby źle, ale łatwo ją przeoczyć, ponieważ kod nadal się kompilował. Jedyny przypadek, w którym użycie.equals()
błędem byłoby, gdyby dane wartości były prymitywne; w takim przypadku kod nie zostałby skompilowany, więc o wiele trudniej go przeoczyć. W związku z tym.equals()
jest o wiele łatwiejszy do zidentyfikowania jako poprawny i bezpieczniejszy w stosunku do przyszłych refaktoryzacji.Właściwie uważam, że język Java powinien zdefiniować == na Objects, aby wywołać .equals () na wartości po lewej stronie i wprowadzić osobny operator dla tożsamości obiektu, ale nie tak zdefiniowano Javę.
Podsumowując, nadal uważam, że argumenty są na rzecz korzystania
.equals()
zenum
typów.źródło
enum
jakoswitch
celu, aby był trochę wyjątkowy.String
s też są trochę wyjątkowi.Wolę używać
==
zamiastequals
:Innym powodem, oprócz innych omówionych tutaj, jest to, że możesz wprowadzić błąd, nie zdając sobie z tego sprawy. Załóżmy, że masz wyliczenia, które są dokładnie takie same, ale w oddzielnych pakakach (nie jest to powszechne, ale może się zdarzyć):
Pierwsze wyliczenie :
Drugie wyliczenie:
Następnie załóżmy, że korzystać z równych jak następny w
item.category
którym jestfirst.pckg.Category
jednak importować drugą enum (second.pckg.Category
) zamiast pierwszy nie wiedząc o tym:Więc zawsze otrzymacie
false
należność, to jest inny wyliczenie, chociaż można oczekiwać, że to prawda, ponieważitem.getCategory()
jestJAZZ
. I może to być trochę trudne do zobaczenia.Jeśli więc zamiast tego użyjesz operatora
==
, wystąpi błąd kompilacji:źródło
Oto przybliżony test czasowy, aby porównać dwa:
Komentuj IFs pojedynczo. Oto dwa porównania z góry w zdemontowanym kodzie bajtowym:
Pierwszy (równy) wykonuje wirtualne wywołanie i testuje wartość logiczną 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 obydwoma IF po jednym na raz. „==” jest zawsze nieco szybszy.
źródło
W przypadku wyliczenia oba są poprawne i słuszne !!
źródło
tl; dr
Inną opcją jest
Objects.equals
metoda użyteczności.Objects.equals
dla zerowego bezpieczeństwaTrzecią opcją jest
equals
metoda statyczna znaleziona wObjects
klasie narzędzia dodanej do Java 7 i nowszych.Przykład
Oto przykład użycia
Month
wyliczenia.Korzyści
Znajduję kilka zalet tej metody:
true
false
NullPointerException
Jak to działa
Jaka jest logika
Objects.equals
?Przekonaj się sam, od kodu Java 10 źródłowego z OpenJDK :
źródło
Używanie czegokolwiek innego niż
==
porównywanie stałych wyliczeniowych jest nonsensem. To jak porównywanieclass
obiektówequals
- nie rób tego!Wystąpił jednak paskudny błąd (BugId 6277781 ) w Sun JDK 6u10 i wcześniejszych, który może być interesujący z powodów historycznych. Ten błąd uniemożliwiał prawidłowe użycie
==
zdezrializowanych wyliczeń, chociaż jest to prawdopodobnie nieco narożny przypadek.źródło
Wyliczenia są klasami, które zwracają jedną instancję (jak singletony) dla każdej stałej wyliczenia zadeklarowanej przez
public static final field
(niezmienne), dzięki czemu==
operator może być używany do sprawdzania ich równości, zamiast przy użyciuequals()
metodyźródło
Powodem, dla którego wyliczenia działają łatwo z ==, jest to, że każda zdefiniowana instancja jest także singletonem. Tak więc porównanie tożsamości za pomocą == zawsze będzie działać.
Ale użycie ==, ponieważ działa z wyliczeniami, oznacza, że cały kod jest ściśle powiązany z użyciem tego wyliczenia.
Na przykład: Enums mogą implementować interfejs. Załóżmy, że obecnie używasz wyliczenia, które implementuje interfejs1. Jeśli później ktoś to zmieni lub wprowadzi nową klasę Impl1 jako implementację tego samego interfejsu. Następnie, jeśli zaczniesz używać instancji Impl1, będziesz mieć dużo kodu do zmiany i przetestowania z powodu wcześniejszego użycia ==.
Dlatego najlepiej postępować zgodnie z dobrą praktyką, chyba że istnieje jakikolwiek uzasadniony zysk.
źródło
Jedną z zasad Sonaru jest
Enum values should be compared with "=="
. Powody są następujące:Wreszcie,
==
wyliczenia na enum są prawdopodobnie bardziej czytelne (mniej gadatliwe) niżequals()
.źródło
Krótko mówiąc, oba mają zalety i wady.
Z jednej strony ma zalety w użyciu
==
, jak opisano w innych odpowiedziach.Z drugiej strony, jeśli z jakiegoś powodu zastąpisz wyliczenia innym podejściem (normalne instancje klasy), po użyciu
==
cię ugryzie. (BTDT.)źródło
Chcę uzupełnić odpowiedź wieloskładnikowe środki smarne:
Osobiście wolę equals (). Ale to sprawdza zgodność typu. Co uważam za ważne ograniczenie.
Aby sprawdzić zgodność typów w czasie kompilacji, zadeklaruj i użyj niestandardowej funkcji w swoim wyliczeniu.
Dzięki temu zyskujesz wszystkie zalety obu rozwiązań: ochronę NPE, łatwy do odczytania kod i kontrolę zgodności typów w czasie kompilacji.
Polecam również dodać wartość UNDEFINED dla wyliczenia.
źródło
==
? Z==
dostać ochronę NPE, łatwy do odczytania kodu i kompilacji typu czas sprawdzania kompatybilności, a otrzymasz wszystkie te bez pisania zwyczaj równa podobny sposób.UNDEFINED
Wartość jest dobrym pomysłem. Oczywiście w Javie 8 użyłbyśOptional
zamiast tego.==
może rzucać,NullPointerException
jeśli typ pierwotny jest porównywany z wersją klasy. Na przykład:źródło
==
nigdy nie może się odnieśćNPEs
. To pytanie ma wystarczającą liczbę odpowiedzi, ale może to być dobre odniesienie na wypadek, gdyby ktoś taki jak ja, który poświęcił 2 godziny, aby dostać się do błędu / funkcji, czyta to.