Comparando los miembros de Java enum: == o equals ()?

Sé que las enumeraciones de Java se comstackn en clases con constructores privados y un grupo de miembros estáticos públicos. Al comparar dos miembros de una enumeración dada, siempre he usado .equals() , por ejemplo

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

Sin embargo, acabo de encontrar un código que usa el operador igual == lugar de .equals ():

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

¿Qué operador es el que debería usar?

Ambos son técnicamente correctos. Si nos fijamos en el código fuente de .equals() , simplemente difiere a == .

Yo uso == , sin embargo, ya que será nulo seguro.

¿Puede == ser utilizado en enum ?

Sí: las enumeraciones tienen controles de instancia ajustados que le permiten usar == para comparar instancias. Aquí está la garantía proporcionada por la especificación del idioma (énfasis mío):

JLS 8.9 Enums

Un tipo enum no tiene más instancias que las definidas por sus constantes enum.

Es un error en tiempo de comstackción intentar instanciar explícitamente un tipo de enumeración. El método de final clone en Enum garantiza que las constantes de la enum nunca se puedan clonar, y el tratamiento especial del mecanismo de serialización garantiza que las instancias duplicadas nunca se creen como resultado de la deserialización. La ejemplificación reflexiva de los tipos enum está prohibida. En conjunto, estas cuatro cosas aseguran que no existan instancias de un tipo de enum más allá de las definidas por las constantes enum .

Debido a que solo hay una instancia de cada constante enum , es permisible usar el operador == en lugar del método equals cuando se comparan dos referencias de objetos si se sabe que al menos uno de ellos se refiere a una constante enum . (El método equals en Enum es un método final que simplemente invoca super.equals en su argumento y devuelve el resultado, realizando así una comparación de identidad.)

Esta garantía es lo suficientemente fuerte como para que Josh Bloch recomiende que si insiste en usar el patrón singleton, la mejor manera de implementarlo es usar una enum un solo elemento (consulte: Effective Java 2nd Edition, Item 3: Aplicar la propiedad singleton con un constructor privado o un tipo enum ; también seguridad de subprocesos en Singleton )


¿Cuáles son las diferencias entre == y equals ?

Como recordatorio, es necesario decir que, en general, == NO es una alternativa viable para los equals . Sin embargo, cuando lo es (como enum ), hay dos diferencias importantes a considerar:

== nunca arroja NullPointerException

 enum Color { BLACK, WHITE }; Color nothing = null; if (nothing == Color.BLACK); // runs fine if (nothing.equals(Color.BLACK)); // throws NullPointerException 

== está sujeto a verificación de compatibilidad de tipo en tiempo de comstackción

 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! 

¿Debe == usarse cuando corresponda?

Bloch menciona específicamente que las clases inmutables que tienen el control adecuado sobre sus instancias pueden garantizar a sus clientes que == es utilizable. enum se menciona específicamente para ejemplificar.

Punto 1: Considere métodos de fábrica estáticos en lugar de constructores

[…] permite que una clase inmutable haga la garantía de que no existen dos instancias iguales: a.equals(b) si y solo si a==b . Si una clase hace esta garantía, sus clientes pueden usar el operador == lugar del método equals(Object) , lo que puede dar como resultado un mejor rendimiento. Los tipos de Enum proporcionan esta garantía.

Para resumir, los argumentos para usar == en enum son:

  • Funciona.
  • Es mas rapido.
  • Es más seguro en tiempo de ejecución.
  • Es más seguro en tiempo de comstackción.

Usar == para comparar dos valores enum funciona porque solo hay un objeto para cada constante enum.

En una nota al margen, en realidad no hay necesidad de usar == para escribir un código seguro nulo si escribes tus equals() esta manera:

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

Esta es una mejor práctica conocida como Compare Constants From The Left que definitivamente debería seguir.

Como otros han dicho, ambos == y .equals() funcionan en la mayoría de los casos. La certeza del tiempo de comstackción de que no está comparando tipos de objetos completamente diferentes que otros han señalado es válida y beneficiosa, sin embargo, el tipo particular de error de comparar objetos de dos tipos diferentes de tiempo de comstackción también sería encontrado por FindBugs (y probablemente por Eclipse / IntelliJ comstack las inspecciones de tiempo), por lo que el comstackdor de Java al encontrarlo no agrega tanta seguridad adicional.

Sin embargo:

  1. El hecho de que == nunca arroje NPE en mi mente es una desventaja de == . Casi nunca debería haber una necesidad de que los tipos enum sean null , ya que cualquier estado adicional que desee express mediante null solo se puede agregar a la enum como una instancia adicional. Si es inesperadamente null , prefiero tener un NPE que == evaluación silenciosa en falso. Por lo tanto, no estoy de acuerdo con que sea más seguro en la opinión en tiempo de ejecución ; es mejor acostumbrarse a nunca dejar que los valores enum sean @Nullable .
  2. El argumento de que == es más rápido también es falso. En la mayoría de los casos, usted llamará a .equals() en una variable cuyo tipo de tiempo de comstackción es la clase enum, y en esos casos el comstackdor puede saber que esto es lo mismo que == (porque el método equals() enum equals() puede no debe ser anulado) y puede optimizar la función llamar lejos. No estoy seguro si el comstackdor hace esto actualmente, pero si no lo hace, y resulta ser un problema de rendimiento en Java en general, entonces prefiero arreglar el comstackdor que tener 100.000 progtwigdores Java que cambien su estilo de progtwigción para adaptarse las características de rendimiento de una versión del comstackdor en particular.
  3. enums son Objetos. Para todos los demás tipos de objeto, la comparación estándar es .equals() , not == . Creo que es peligroso hacer una excepción para enums porque podrías terminar accidentalmente comparando Objetos con == lugar de equals() , especialmente si refactorizas una enum en una clase que no es enum. En el caso de tal refactorización, el punto de trabajo It desde arriba es incorrecto. Para convencerte de que el uso de == es correcto, debes verificar si el valor en cuestión es enum o primitivo; si fuera una clase no enum , sería incorrecto pero fácil pasar por alto porque el código aún se comstackría. El único caso en que un uso de .equals() sería incorrecto es si los valores en cuestión eran primitivos; en ese caso, el código no se comstackría, por lo que es mucho más difícil pasar por alto. Por lo tanto, .equals() es mucho más fácil de identificar como correcto, y es más seguro contra futuras refactorizaciones.

De hecho, creo que el lenguaje Java debería haber definido == en Objetos para llamar a .equals () en el valor de la izquierda e introducir un operador separado para la identidad del objeto, pero no fue así como se definió Java.

En resumen, sigo pensando que los argumentos son a favor de usar .equals() para los tipos enum .

Aquí hay una prueba de tiempo cruda para comparar los dos:

 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 

Comente los IF uno por uno. Aquí están las dos comparaciones de arriba en el código de byte desmontado:

  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 

El primero (igual) realiza una llamada virtual y prueba el retorno booleano de la stack. El segundo (==) compara las direcciones del objeto directamente de la stack. En el primer caso, hay más actividad.

Realicé esta prueba varias veces con ambas IF de a una por vez. El "==" es ligeramente más rápido.

En caso de enum, ambos son correctos y correctos.

Prefiero usar == lugar de equals :

Otra razón, además de las otras ya discutidas aquí, es que podrías introducir un error sin darte cuenta. Supongamos que tiene estas enumeraciones, que es exactamente lo mismo, pero en pacajges separados (no es común, pero podría suceder):

Primera enumeración :

 package first.pckg public enum Category { JAZZ, ROCK, POP, POP_ROCK } 

Segunda enumeración:

 package second.pckg public enum Category { JAZZ, ROCK, POP, POP_ROCK } 

Entonces supongamos que usas los equivalentes como siguiente en item.category que es first.pckg.Category pero importas el segundo enum ( second.pckg.Category ) en vez del primero sin darte cuenta:

 import second.pckg.Category; ... Category.JAZZ.equals(item.getCategory()) 

Por lo tanto, obtendrás siempre el valor false debido a que es una enumeración diferente, aunque esperas que sea cierto porque item.getCategory() es JAZZ . Y podría ser un poco difícil de ver.

Entonces, si usa el operador == , tendrá un error de comstackción:

operator == no se puede aplicar a “second.pckg.Category”, “first.pckg.Category”

 import second.pckg.Category; ... Category.JAZZ == item.getCategory() 

Usar cualquier cosa que no sea == para comparar las constantes enum es una tontería. Es como comparar objetos de class con equals , ¡no lo hagas!

Sin embargo, hubo un error desagradable (BugId 6277781 ) en Sun JDK 6u10 y anterior que podría ser interesante por razones históricas. Este error impidió el uso adecuado de == en las enumeraciones deserializadas, aunque podría decirse que se trata de un caso de esquina.

Los enums son clases que devuelven una instancia (como singletons) para cada constante de enumeración declarada por public static final field (inmutable) para que el operador == pueda usar para verificar su igualdad en lugar de usar el método equals()

En resumen, ambos tienen pros y contras.

Por un lado, tiene ventajas usar == , como se describe en las otras respuestas.

Por otro lado, si por alguna razón reemplazas las enumeraciones con un enfoque diferente (instancias de clases normales), habrás usado == mordiscos. (BTDT)

La razón por la que las enumeraciones funcionan fácilmente con == es porque cada instancia definida también es un singleton. Entonces la comparación de identidad usando == siempre funcionará.

Pero usar == porque funciona con enumeraciones significa que todo tu código está estrechamente relacionado con el uso de esa enumeración.

Por ejemplo: Enums puede implementar una interfaz. Supongamos que está utilizando una enumeración que implementa Interface1. Si más adelante, alguien lo cambia o introduce una nueva clase Impl1 como una implementación de la misma interfaz. Luego, si comienza a usar instancias de Impl1, tendrá mucho código para cambiar y probar debido al uso anterior de ==.

Por lo tanto, es mejor seguir lo que se considera una buena práctica a menos que haya una ganancia justificable.

Quiero complementar la respuesta de polygenelubricants:

Personalmente prefiero igualdad (). Pero el lago es el tipo de verificación de compatibilidad. Lo cual creo que es una limitación importante.

Para tener una verificación de compatibilidad de tipo en tiempo de comstackción, declare y use una función personalizada en su enumeración.

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

Con esto, tiene todas las ventajas de ambas soluciones: protección NPE, código fácil de leer y verificación de compatibilidad de tipo en tiempo de comstackción.

También recomiendo agregar un valor INDEFINIDO para enum.

Puede usar: ==, equals () o switch () para comparar Enums, todos son técnicamente verdaderos y servirían a su necesidad.

Consulte este tutorial para saber más sobre las operaciones comunes de Enum: Cómo usar Enums en Java

Enum en el medio es un conjunto de enteros constantes. “==” es tan válido y correcto como si comparas dos enteros.

Me gustaría resaltar explícitamente esta diferencia específica entre el operador == y el método equals() :

El método equals() está destinado a verificar si los contenidos de los objetos a los que se refieren las variables de referencia son los mismos.

El operador == verifica si la (s) variable (s) de referencia implica (n) referencia (s) al mismo objeto .

Depende de la clase implementadora proporcionar esta diferenciación según lo necesite la aplicación.

De lo contrario, el comportamiento predeterminado será el proporcionado por la clase Object (en Java), tal como se explica en http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals ( java.lang.Object) :

El método equals para la clase Object implementa la relación de equivalencia más discriminatoria posible en los objetos; es decir, para cualquier valor de referencia no nulo x e y , este método devuelve true si y solo si y refieren al mismo objeto ( x == y tiene el valor true ).