Vraag Vergelijken van Java enum-leden: == of is gelijk aan ()?


Ik weet dat Java-enums zijn gecompileerd tot klassen met privéconstructeurs en een hoop openbare statische leden. Wanneer ik twee leden van een gegeven opsomming vergelijk, heb ik dat altijd gebruikt .equals()b.v.

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

Ik kwam echter net een code tegen die de operator equals gebruikt == in plaats van .equals ():

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

Welke operator moet ik gebruiken?


1418
2017-11-17 17:26


oorsprong


antwoorden:


Beide zijn technisch correct. Als je naar de broncode kijkt .equals(), het verslaat eenvoudigweg naar ==.

ik gebruik ==echter, omdat dat niet veilig zal zijn.


1276
2017-11-17 17:29



Kan == worden gebruikt op enum?

Ja: enums hebben een strakke instantie die u kunt gebruiken == om instanties te vergelijken. Hier is de garantie geboden door de taalspecificatie (nadruk door mij):

JLS 8.9 Enums

Een enum-type heeft geen andere instanties dan degene die worden gedefinieerd door zijn enum-constanten.

Het is een compileerfout om te proberen een enum-type expliciet te maken. De final clone methode in Enum verzekerd dat enum constanten kunnen nooit worden gekloond en de speciale behandeling door het serialisatiemechanisme zorgt ervoor dat duplicaten nooit worden gemaakt als gevolg van deserialisatie. Reflecterende instantiatie van typen enum is verboden. Samen zorgen deze vier dingen ervoor dat geen instanties van een enum Type bestaat verder dan die gedefinieerd door de enum constanten.

Omdat er maar één exemplaar van elk is enum constante, het is toegestaan ​​om de == operator in plaats van de equals methode bij het vergelijken van twee objectreferenties als bekend is dat ten minste één verwijst naar een enum constante. (De equals methode in Enum is een final methode die alleen maar aanroept super.equals op zijn argument en retourneert het resultaat, waardoor een identiteitsvergelijking wordt uitgevoerd.)

Deze garantie is sterk genoeg dat Josh Bloch aanbeveelt, dat als je erop staat het singleton-patroon te gebruiken, de beste manier om het te implementeren het gebruik van een enkel element is. enum (zien: Effectieve Java 2nd Edition, Item 3: Dwing de singleton-property af met een private constructor of een type type; ook Veiligheid in Singleton)


Wat zijn de verschillen tussen == en equals?

Ter herinnering: in het algemeen moet worden gezegd dat == is GEEN haalbaar alternatief voor equals. Wanneer het is, echter (zoals met enum), zijn er twee belangrijke verschillen om te overwegen:

== gooit nooit NullPointerException

enum Color { BLACK, WHITE };

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

== is onderworpen aan type compatibiliteitscontrole tijdens het compileren

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!

moeten == worden gebruikt indien van toepassing?

Bloch vermeldt specifiek dat onveranderlijke klassen die de juiste controle hebben over hun instanties dat kunnen garanderen aan hun klanten == is bruikbaar. enum wordt specifiek vermeld om als voorbeeld te dienen.

Item 1: Overweeg statische fabrieksmethoden in plaats van constructeurs

[...] het staat een onveranderlijke klasse toe om de garantie te geven dat er geen twee gelijke instanties bestaan: a.equals(b)als en alleen als a==b. Als een klasse deze garantie biedt, kunnen haar klanten de == operator in plaats van de equals(Object) methode, wat kan resulteren in betere prestaties. Enum-typen bieden deze garantie.

Samenvattend, de argumenten voor het gebruik == op enum zijn:

  • Het werkt.
  • Het is sneller.
  • Het is veiliger tijdens runtime.
  • Het is veiliger tijdens het compileren.

964
2018-05-30 04:38



Gebruik makend van == om twee opsommingswaarden te vergelijken, werkt omdat er maar één object is voor elke enum-constante.

In een kanttekening is er eigenlijk geen noodzaak om te gebruiken == om een ​​nul-veilige code te schrijven als je jouw schrijft equals() soortgelijk:

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

Dit is een best practice bekend als Constanten vergelijken van links dat je zeker zou moeten volgen.


72
2017-11-17 17:30



Zoals anderen hebben gezegd == en .equals() werk in de meeste gevallen. De compileertijd zekerheid dat je niet volledig verschillende soorten objecten vergelijkt die anderen hebben aangegeven, is geldig en nuttig, maar het specifieke soort bug van het vergelijken van objecten van twee verschillende compileertijden kan ook gevonden worden door FindBugs (en waarschijnlijk door Eclipse / IntelliJ compileertijdinspecties), zodat de Java-compiler die het vindt, niet zoveel extra veiligheid toevoegt.

Echter:

  1. Het feit dat == gooit nooit NPE in mijn gedachten is een nadeel van ==. Er zou bijna nooit behoefte aan moeten zijn enum soorten te zijn null, omdat elke extra status die u via wilt uitdrukken null kan gewoon worden toegevoegd aan de enum als een extra instantie. Als het onverwacht is null, Ik heb liever een NPE dan == stil evalueren tot onwaar. Daarom ben ik het niet eens met de het is veiliger tijdens runtime mening; het is beter om de gewoonte aan te gaan om nooit te laten enum waarden zijn @Nullable.
  2. Het argument dat == is sneller is ook nep. In de meeste gevallen bel je .equals() op een variabele waarvan het compileertijdstype de enumklasse is, en in die gevallen kan de compiler weten dat dit hetzelfde is als == (omdat een enum's equals() methode kan niet worden overschreven) en kan de functieoproep optimaliseren. Ik weet niet zeker of de compiler dit momenteel doet, maar als dat niet het geval is en het blijkt een prestatieprobleem in Java te zijn, dan repareer ik de compiler liever dan dat 100.000 Java-programmeurs hun programmeerstijl aanpassen de prestatiekenmerken van een bepaalde compilerversie.
  3. enums zijn Objecten. Voor alle andere objecttypen is de standaardvergelijking .equals(), niet ==. Ik denk dat het gevaarlijk is om een ​​uitzondering voor te maken enums omdat je per ongeluk objecten kunt vergelijken met == in plaats van equals(), vooral als je een refactor bent enum naar een niet-opslaande klas. In geval van een dergelijke refactoring, de Het werkt punt van boven is verkeerd. Om jezelf ervan te overtuigen dat een gebruik van == is correct, u moet controleren of de betreffende waarde een van beide is enum of een primitief; als het een niet-enumklasse, het zou verkeerd zijn maar gemakkelijk te missen omdat de code nog zou compileren. Het enige geval wanneer een gebruik van .equals() zou fout zijn als de waarden in kwestie primitieven waren; in dat geval zou de code niet compileren, dus het is veel moeilijker om te missen. Vandaar, .equals() is veel gemakkelijker te identificeren als correct en veiliger tegen toekomstige refactorings.

Ik denk eigenlijk dat de Java-taal == had moeten definiëren over Objects om .equals () op de linkerhandwaarde te roepen, en een aparte operator voor objectidentiteit te introduceren, maar dat is niet hoe Java werd gedefinieerd.

Samengevat denk ik nog steeds dat de argumenten voor gebruik zijn .equals() voor enum soorten.


56
2018-02-14 11:59



Hier is een ruwe timingtest om de twee te vergelijken:

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

}

Geef de IF's een voor een een commentaar. Hier zijn de twee vergelijkingen van hierboven in gedemonteerde 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

De eerste (gelijk aan) voert een virtueel gesprek uit en test de teruggekeerde boolean uit de stapel. De tweede (==) vergelijkt de objectadressen rechtstreeks van de stapel. In het eerste geval is er meer activiteit.

Ik heb deze test een aantal keer met beide IF's één voor één uitgevoerd. De "==" is ooit zo iets sneller.


12
2017-10-14 21:19



In het geval van enum zijn beide correct en goed !!


10
2017-11-17 17:28



Ik geef de voorkeur om te gebruiken == in plaats van equals:

Andere redenen, naast de anderen die hier al zijn besproken, is dat je een bug kunt introduceren zonder het te beseffen. Stel dat je deze enums hebt die precies hetzelfde is, maar in gescheiden pacakges (het is niet gebruikelijk, maar het kan gebeuren):

Eerste opsomming:

package first.pckg

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

Tweede opsomming:

package second.pckg

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

Stel dan dat je de gelijken zoals de volgende in gebruikt item.category welke is first.pckg.Category maar je importeert de tweede enum (second.pckg.Category) in plaats daarvan de eerste zonder het te beseffen:

import second.pckg.Category;
...

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

Dus je zal altijd komen false due is een andere enum, hoewel je verwacht waar omdat item.getCategory() is JAZZ. En het kan een beetje moeilijk zijn om te zien.

Dus, als u in plaats daarvan de operator gebruikt == je hebt een compilatiefout:

operator == kan niet worden toegepast op "second.pckg.Category", "first.pckg.Category"

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

10
2018-04-21 06:47



Gebruik van iets anders dan == het vergelijken van enum-constanten is onzin. Het is als vergelijken class objecten met equals - doe het niet!

Er was echter een vervelende bug (BugId 6277781) in Sun JDK 6u10 en eerder dat om historische redenen interessant zou kunnen zijn. Deze bug heeft het juiste gebruik van verhinderd == op gedeserialiseerde enums, hoewel dit misschien wel een beetje een hoekzaak is.


4
2017-10-26 20:40



Enums zijn klassen die één exemplaar retourneren (zoals singletons) voor elke opsommingsconstante die door wordt verklaard public static final field (onveranderlijk) dus dat == operator zou kunnen worden gebruikt om hun gelijkheid te controleren in plaats van te gebruiken equals() methode


4
2017-11-07 15:21