with - java enum equals




Confronto tra membri di enum Java:== o equals()? (10)

So che le enumerazioni Java sono compilate in classi con costruttori privati ​​e un gruppo di membri statici pubblici. Quando si confrontano due membri di una data enum, ho sempre usato .equals() , ad es

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

Tuttavia, ho appena trovato un codice che utilizza l'operatore di uguale == invece di .equals ():

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

Quale operatore dovrei usare?


tl; dr

Un'altra opzione è il metodo di utilità Objects.equals .

Objects.equals( thisEnum , thatEnum )

Objects.equals

equivale all'operatore == invece di .equals ()

Quale operatore dovrei usare?

Una terza opzione è il metodo static Objects.equals trovato nella classe di utilità Objects aggiunta a Java 7 e versioni successive.

Esempio

Ecco un esempio usando l'enum del Month .

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

Benefici

Trovo un paio di vantaggi con questo metodo:

  • Null-sicurezza
  • Compatto, leggibile

Come funziona

Qual è la logica utilizzata da Objects.equals ?

Guarda tu stesso, dal codice sorgente Java 10 di OpenJDK :

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

Può essere usato == su enum ?

Sì: le enumerazioni hanno controlli di istanze strette che ti consentono di usare == per confrontare le istanze. Ecco la garanzia fornita dalle specifiche del linguaggio (sottolineatura da parte mia):

JLS 8.9 Enums

Un tipo enum non ha altre istanze oltre a quelle definite dalle sue costanti enum.

È un errore in fase di compilazione per tentare di istanziare esplicitamente un tipo di enum. Il metodo final clone in Enum garantisce che le costanti enum non possano mai essere clonate e il trattamento speciale mediante il meccanismo di serializzazione garantisce che le istanze duplicate non vengano mai create a seguito della deserializzazione. L'istanziazione riflessiva dei tipi di enum è proibita. Insieme, queste quattro cose assicurano che non esistono istanze di un tipo enum oltre a quelle definite dalle costanti enum .

Poiché esiste una sola istanza di ciascuna costante enum , è consentito utilizzare l'operatore == al posto del metodo equals quando si confrontano due riferimenti a oggetti se è noto che almeno uno di essi fa riferimento a una costante enum . (Il metodo equals in Enum è un metodo final che richiama semplicemente super.equals sul suo argomento e restituisce il risultato, eseguendo così un confronto di identità.)

Questa garanzia è abbastanza forte che Josh Bloch raccomanda, che se insisti ad usare il modello singleton, il modo migliore per implementarlo è usare un enum elemento singolo (vedi: Java 2a Edizione efficace, Articolo 3: Applicare la proprietà singleton con un costruttore privato o un tipo enum , anche sicurezza thread in Singleton )

Quali sono le differenze tra == ed equals ?

Come promemoria, bisogna dire che generalmente, == NON è un'alternativa praticabile agli equals . Quando lo è, tuttavia (come con enum ), ci sono due importanti differenze da considerare:

== non getta mai NullPointerException

enum Color { BLACK, WHITE };

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

== è soggetto al controllo di compatibilità del tipo in fase di compilazione

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!

Dovrebbe essere usato == quando applicabile?

Bloch menziona specificamente che le classi immutabili che hanno il controllo adeguato sulle loro istanze possono garantire ai loro clienti che == è utilizzabile. enum è specificamente menzionato per esemplificare.

Articolo 1: considera i metodi di factory statici anziché i costruttori

[...] consente a una classe immutabile di garantire che non esistano due casi uguali: a.equals(b) se e solo se a==b . Se una classe fa questa garanzia, i suoi clienti possono usare l'operatore == invece del metodo equals(Object) , che può comportare un miglioramento delle prestazioni. I tipi Enum forniscono questa garanzia.

Per riassumere, gli argomenti per l'utilizzo di == su enum sono:

  • Funziona.
  • È più veloce
  • È più sicuro in fase di esecuzione.
  • È più sicuro in fase di compilazione.

Ecco un crudo test di temporizzazione per confrontare i due:

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

}

Commenta gli IF uno alla volta. Ecco i due confronti da sopra in byte-code disassemblato:

 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

Il primo (uguale) esegue una chiamata virtuale e verifica il booleano di ritorno dallo stack. Il secondo (==) confronta gli indirizzi degli oggetti direttamente dallo stack. Nel primo caso c'è più attività.

Ho eseguito questo test più volte con entrambi gli IF uno alla volta. "==" è sempre leggermente più veloce.


Entrambi sono tecnicamente corretti. Se si guarda il codice sorgente per .equals() , si limita a == .

Io uso == , tuttavia, poiché sarà nullo.


Il motivo per cui le enumerazioni funzionano facilmente con == è perché ogni istanza definita è anche un singleton. Quindi il confronto dell'identità usando == funzionerà sempre.

Ma usando == perché funziona con le enumerazioni, tutto il tuo codice è strettamente connesso all'uso di quell'enumerazione.

Ad esempio: Enums può implementare un'interfaccia. Supponiamo che tu stia attualmente utilizzando un enum che implementa Interface1. Se successivamente, qualcuno lo cambia o introduce una nuova classe Impl1 come implementazione della stessa interfaccia. Quindi, se inizi a utilizzare istanze di Impl1, avrai un sacco di codice da cambiare e testare a causa dell'uso precedente di ==.

Quindi, è meglio seguire ciò che è considerato una buona pratica a meno che non vi sia alcun guadagno giustificabile.


In breve, entrambi hanno pro e contro.

Da un lato, presenta vantaggi == , come descritto nelle altre risposte.

D'altra parte, se per qualsiasi motivo sostituisci l'enumerazione con un approccio diverso (normali istanze di classe), dopo aver usato == ti morde. (BTDT.)


Le enumerazioni sono classi che restituiscono un'istanza (come i singleton) per ogni costante di enumerazione dichiarata dal public static final field (immutabile) in modo che l'operatore == possa essere utilizzato per verificare l'uguaglianza piuttosto che utilizzare il metodo equals()


Preferisco usare == invece di equals :

Altro motivo, oltre agli altri già discussi qui, è che potresti introdurre un bug senza rendertene conto. Supponiamo che tu abbia questa enumerazione che è esattamente la stessa, ma in pacakges separati (non è comune, ma potrebbe accadere):

Primo enum :

package first.pckg

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

Secondo enum:

package second.pckg

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

Quindi supponiamo di utilizzare gli equivalenti come next in item.category che è first.pckg.Category ma si importa il secondo enum ( second.pckg.Category ) invece il primo senza rendersene conto:

import second.pckg.Category;
...

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

Quindi otterrai sempre false perché è un enum diverso, anche se ti aspetti vero perché item.getCategory() è JAZZ . E potrebbe essere un po 'difficile da vedere.

Quindi, se invece usi l'operatore == avresti un errore di compilazione:

operator == non può essere applicato a "second.pckg.Category", "first.pckg.Category"

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

Usare qualcosa di diverso da == per confrontare le costanti enum non ha senso. È come confrontare oggetti di class con equals : non farlo!

Tuttavia, c'era un brutto bug (BugId 6277781 ) in Sun JDK 6u10 e precedenti che potrebbe essere interessante per ragioni storiche. Questo bug ha impedito l'uso corretto di == sulle enumerazioni deserializzate, sebbene questo sia probabilmente un po 'un caso isolato.


Voglio completare la risposta dei poligenelubrificanti:

Personalmente preferisco gli uguali (). Ma fa il controllo di compatibilità di tipo. Quale penso sia una limitazione importante.

Per verificare la compatibilità dei tipi al momento della compilazione, dichiarare e utilizzare una funzione personalizzata nell'enumerazione.

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

Con questo, hai ottenuto tutti i vantaggi di entrambe le soluzioni: protezione NPE, codice di facile lettura e verifica della compatibilità del tipo in fase di compilazione.

Raccomando anche di aggiungere un valore UNDEFINED per enum.





enums