recorrer - Comparando miembros enum de Java:== o es igual a()?
java enum string (15)
Sé que las enumeraciones de Java se compilan en clases con constructores privados y un grupo de miembros estáticos públicos. Al comparar dos miembros de una enumeración determinada, 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 estar usando?
tl; dr
Otra opción es el método de utilidad Objects.equals
.
Objects.equals( thisEnum , thatEnum )
Objects.equals
operador igual == en lugar de .equals ()
¿Qué operador es el que debería estar usando?
Una tercera opción es el método estático de Objects.equals que se encuentra en la clase de utilidad Objects
agregada a Java 7 y posterior.
Ejemplo
Aquí hay un ejemplo usando la enumeración del Month
.
boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ; // Returns `false`.
Beneficios
Encuentro un par de beneficios para este método:
- Seguridad nula
- Ambos nulos ➙
true
- O bien nulo ➙
false
- No hay riesgo de lanzar
NullPointerException
- Ambos nulos ➙
- Compacto, legible
Cómo funciona
¿Cuál es la lógica utilizada por Objects.equals
?
Compruébelo usted mismo, desde el código fuente de Java 10 de OpenJDK :
return (a == b) || (a != null && a.equals(b));
¿Se puede usar ==
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 por mí):
JLS 8.9 Enums
Un tipo de enumeración no tiene más instancias que las definidas por sus constantes de enumeración.
Es un error de tiempo de compilación intentar crear una instancia explícita de un tipo de enumeración. El
final clone
método definal clone
enEnum
garantiza que las constantes deenum
nunca se puedan clonar, y el tratamiento especial por parte del mecanismo de serialización garantiza que las instancias duplicadas nunca se creen como resultado de la deserialización. La creación de instancias reflexivas de tipos de enumeración está prohibida. En conjunto, estas cuatro cosas garantizan que no existan instancias de un tipo deenum
más allá de las definidas por las constantes deenum
.Debido a que solo hay una instancia de cada constante de
enum
, es permisible utilizar el operador==
en lugar del métodoequals
al comparar dos referencias de objetos si se sabe que al menos una de ellas se refiere a una constante deenum
. (El métodoequals
enEnum
es un métodofinal
que simplemente invoca asuper.equals
en su argumento y devuelve el resultado, por lo tanto, realiza una comparación de identidad).
Esta garantía es lo suficientemente fuerte como Josh Bloch recomienda, que si insistes en usar el patrón de singleton, la mejor manera de implementarlo es usar una enum
un solo elemento (ver: Efectiva Java 2ª edición, elemento 3: aplicar la propiedad de singleton con un constructor privado o un tipo de enumeración ; también Thread safety en Singleton )
¿Cuáles son las diferencias entre ==
y equals
?
Como recordatorio, se debe decir que, en general, ==
NO es una alternativa viable a los equals
. Sin embargo, cuando lo es (como con enum
), hay dos diferencias importantes que se deben tener en cuenta:
==
nunca lanza 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 compilació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 sea aplicable?
Bloch menciona específicamente que las clases inmutables que tienen un control adecuado sobre sus instancias pueden garantizar a sus clientes que ==
es utilizable. enum
se menciona específicamente para ejemplificar.
Ítem 1: considerar métodos de fábrica estáticos en lugar de constructores
[...] permite que una clase inmutable garantice que no existen dos instancias iguales:
a.equals(b)
si y solo sia==b
. Si una clase hace esta garantía, sus clientes pueden usar el operador==
lugar del métodoequals(Object)
, lo que puede resultar en un mejor rendimiento. Los tipos de enumeración 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 compilación.
Ambos son técnicamente correctos. Si miras el código fuente de .equals()
, simplemente difiere a ==
.
Yo uso ==
, sin embargo, ya que será nulo seguro.
Aquí hay una prueba de tiempo crudo 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<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());
}
}
Comente los IF uno por uno. Aquí están las dos comparaciones de arriba en el código de bytes 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 a) realiza una llamada virtual y prueba el retorno booleano desde la pila. El segundo (==) compara las direcciones de los objetos directamente desde la pila. En el primer caso hay más actividad.
Hice esta prueba varias veces con ambos IF uno a la vez. El "==" es un poco más rápido.
Como han dicho otros, tanto ==
como .equals()
funcionan en la mayoría de los casos. La certeza del tiempo de compilación de que no está comparando tipos de Objetos diferentes que otros han señalado es válida y beneficiosa, sin embargo, FindBugs también encontraría el tipo particular de error al comparar objetos de dos tipos diferentes de tiempo de compilación (y probablemente de Inspecciones de tiempo de compilación de Eclipse / IntelliJ), por lo que el compilador de Java que lo encuentra no agrega tanta seguridad adicional.
Sin embargo:
- El hecho de que
==
nunca arroje NPE en mi mente es una desventaja de==
. Casi nunca debería ser necesario que los tipos deenum
seannull
, ya que cualquier estado adicional que desee expresar a través denull
solo se puede agregar a laenum
como una instancia adicional. Si es inesperadamentenull
, prefiero tener una NPE que==
evaluar silenciosamente a falso. Por lo tanto, no estoy de acuerdo con que es más seguro en la opinión de tiempo de ejecución ; es mejor adquirir el hábito de no dejar nunca que los valores deenum
sean@Nullable
. - El argumento de que
==
es más rápido también es falso. En la mayoría de los casos, llamará a.equals()
en una variable cuyo tipo de tiempo de compilación sea la clase enum, y en esos casos el compilador puede saber que esto es igual a==
(porque el métodoequals()
unaenum
puede no ser anulado) y puede optimizar la función de llamada de distancia. No estoy seguro de si el compilador actualmente hace esto, pero si no lo hace, y resulta ser un problema de rendimiento en Java en general, entonces prefiero arreglar el compilador que hacer que 100.000 programadores de Java cambien su estilo de programación para adaptarse Características de rendimiento de una versión particular del compilador. -
enums
son objetos. Para todos los demás tipos de objetos, la comparación estándar es.equals()
, no==
. Creo que es peligroso hacer una excepción para lasenums
porque podría terminar comparando Objetos accidentalmente con==
lugar deequals()
, especialmente si refactoriza unaenum
en una clase que no es enumeración. En el caso de una refactorización de este tipo, el punto de trabajo desde arriba es incorrecto. Para convencerse de que el uso de==
es correcto, debe comprobar si el valor en cuestión es unaenum
o una primitiva; Si seenum
clase que no seaenum
, sería incorrecto pero fácil de omitir porque el código aún se compilaría. El único caso en el que un uso de.equals()
sería incorrecto es si los valores en cuestión fueran primitivos; en ese caso, el código no se compilaría, por lo que es mucho más difícil pasarlo por alto. Por lo tanto,.equals()
es mucho más fácil de identificar como correcto, y es más seguro contra futuras refactorizaciones.
Realmente creo que el lenguaje Java debería haber definido == en Objetos para llamar a .equals () en el valor de la mano izquierda, e introducir un operador separado para la identidad del objeto, pero no es así como se definió Java.
En resumen, sigo pensando que los argumentos están a favor de usar .equals()
para los tipos de enum
.
En caso de enumeración ambos son correctos y correctos !!
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 reemplaza las enumeraciones con un enfoque diferente (instancias de clase normales), habiendo usado ==
te muerde. (BTDT.)
Enumeración en el medio es un conjunto de enteros constantes. "==" es tan válido y apropiado como si compararas dos enteros.
La razón por la que las enumeraciones funcionan fácilmente con == es porque cada instancia definida también es un singleton. Así que la comparación de identidad usando == siempre funcionará.
Pero usar == porque funciona con enumeraciones significa que todo su código está estrechamente relacionado con el uso de esa enumeración.
Por ejemplo: Enums puede implementar una interfaz. Supongamos que actualmente 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 alguna ganancia justificable.
Las enumeraciones son clases que devuelven una instancia (como singletons) para cada constante de enumeración declarada por public static final field
(inmutable), de modo que el operador ==
podría usarse para verificar su igualdad en lugar de usar el método equals()
Me gustaría resaltar explícitamente esta diferencia específica entre el método ==
operator y equals()
:
El método equals()
está destinado a verificar si el contenido de los objetos a los que se refieren las variables de referencia involucradas es el mismo.
El operador ==
comprueba si las variables de referencia involucradas se refieren al mismo objeto .
Depende de la clase implementadora proporcionar esta diferenciación según lo requiera la aplicación.
De lo contrario, el comportamiento predeterminado será el proporcionado por la clase de Object
(en Java) donde 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 claseObject
implementa la relación de equivalencia más discriminatoria posible en los objetos; es decir, para cualquier valor de referencia no nuloy
, este método devuelvetrue
si y solo six
ey
refieren al mismo objeto (x == y
tiene el valortrue
).
Prefiero usar ==
lugar de equals
:
Otra razón, además de las otras ya comentadas aquí, es que puedes introducir un error sin darte cuenta. Supongamos que tiene esta lista que es exactamente igual pero en paquetes 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
}
Luego, suponga que utiliza los iguales como los siguientes en item.category
que es first.pckg.Category
pero importa el segundo enum ( second.pckg.Category
) en lugar del primero sin darse cuenta:
import second.pckg.Category;
...
Category.JAZZ.equals(item.getCategory())
Por lo tanto, siempre obtendrás false
porque 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 en cambio usa el operador ==
, tendrá un error de compilación:
operator == no se puede aplicar a "second.pckg.Category", "first.pckg.Category"
import second.pckg.Category;
...
Category.JAZZ == item.getCategory()
Quiero complementar la respuesta de poligenelubricantes:
Yo personalmente prefiero iguales (). Pero es la prueba de compatibilidad de tipos. Que creo que es una limitación importante.
Para tener una verificación de compatibilidad de tipos en el momento de la compilació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 tipos en el momento de la compilación.
También recomiendo agregar un valor UNDEFINED para enum.
Usar ==
para comparar dos valores de enumeración funciona porque solo hay un objeto para cada constante de enumeración.
En una nota al margen, en realidad no hay necesidad de usar ==
para escribir código seguro nulo si escribes tus equals()
esta manera:
public useEnums(SomeEnum a)
{
if(SomeEnum.SOME_ENUM_VALUE.equals(a))
{
...
}
...
}
Esta es una práctica recomendada conocida como Comparar constantes de la izquierda que definitivamente debe seguir.
Usar otra cosa que no sea ==
para comparar constantes de enumeración 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 anteriores que podría ser interesante por razones históricas. Este error impidió el uso adecuado de ==
en enumeraciones deserializadas, aunque esto podría decirse que es un caso de esquina.