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


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 dada, siempre he usado .equals(), p.ej.

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

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

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

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


1418
2017-11-17 17:26


origen


Respuestas:


Ambos son técnicamente correctos. Si miras el código fuente para .equals(), simplemente difiere a ==.

yo suelo ==, sin embargo, ya que será nulo seguro.


1276
2017-11-17 17:29



Poder == 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 compilación intentar instanciar explícitamente un tipo de enumeración. los final clone método en Enum asegura que enum las constantes nunca se pueden 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 hay instancias de un enum tipo existen más allá de los definidos por el enum constantes.

Porque solo hay una instancia de cada enum constante, es permisible usar el == operador en lugar de equals método cuando se comparan dos referencias de objetos si se sabe que al menos uno de ellos se refiere a un enum constante. (Los equals método en Enum es un final método 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 un elemento único. enum (ver: Effective Java 2nd Edition, Item 3: Imponer la propiedad singleton con un constructor privado o un tipo enum; además Seguridad de subprocesos en Singleton)


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

Como recordatorio, debe decirse que, en general, == NO es una alternativa viable a equals. Cuando lo es, sin embargo (como con enum), hay dos diferencias importantes a considerar:

== nunca tira 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!

Debería == ser utilizado cuando corresponda?

Bloch menciona específicamente que las clases inmutables que tienen un control adecuado sobre sus instancias pueden garantizar a sus clientes que == es usable 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, entonces sus clientes pueden usar la == operador en lugar de equals(Object) método, que puede resultar en 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 compilación.

964
2018-05-30 04:38



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

En una nota lateral, en realidad no hay necesidad de usar == escribir código seguro nulo si escribe su equals() Me gusta esto:

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

Esta es una mejor práctica conocida como Compare las constantes de la izquierda que definitivamente deberías seguir


72
2017-11-17 17:30



Como otros han dicho, ambos == y .equals() trabajo en la mayoría de los casos. La certeza del tiempo de compilació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 compilación también sería encontrado por FindBugs (y probablemente por Eclipse / IntelliJ compila las inspecciones de tiempo), por lo que el compilador de Java al encontrarlo no agrega tanta seguridad adicional.

Sin embargo:

  1. El hecho de que == Nunca arroja NPE en mi mente es una desventaja de ==. Casi nunca debería haber una necesidad de enum tipos para ser null, ya que cualquier estado extra que desee expresar a través de null solo se puede agregar a la enum como una instancia adicional. Si es inesperadamente null, Prefiero tener un NPE que == evaluando silenciosamente a falso. Por lo tanto, no estoy de acuerdo con el es más seguro en tiempo de ejecución opinión; es mejor acostumbrarse nunca a dejar enum los valores sean @Nullable.
  2. El argumento que == es Más rápido también es falso En la mayoría de los casos llamarás .equals() en una variable cuyo tipo de tiempo de compilación es la clase enum, y en esos casos el compilador puede saber que esto es lo mismo que == (porque un enumes equals() método no puede ser anulado) y puede optimizar la función llamada de distancia. No estoy seguro si el compilador hace esto actualmente, pero si no lo hace, y resulta ser un problema de rendimiento en Java en general, entonces prefiero arreglar el compilador que tener 100.000 programadores Java que cambien su estilo de programación para adaptarse las características de rendimiento de una versión del compilador en particular.
  3. 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 enums porque podrías terminar accidentalmente comparando objetos con == en lugar de equals(), especialmente si usted refactoriza enum en una clase no enum. En caso de tal refactorización, el Funciona el punto de arriba es incorrecto Para convencerse de que un uso de == es correcto, debe verificar si el valor en cuestión es un enum o un primitivo; si fuera un no-enumclase, sería incorrecto pero fácil pasar por alto porque el código todavía se compilaría. El único caso cuando un uso de .equals() Sería erróneo 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 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 enum tipos.


56
2018-02-14 11:59



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<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 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 pila. El segundo (==) compara las direcciones del objeto directamente de la pila. 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.


12
2017-10-14 21:19



En caso de enum, ambos son correctos y correctos.


10
2017-11-17 17:28



Prefiero usar == en 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 iguales como el siguiente en item.category cual es first.pckg.Category pero importas la segunda enumeración (second.pckg.Category) en cambio, el primero sin darse cuenta:

import second.pckg.Category;
...

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

Así que obtendrás siempre false debido es una enumeración diferente aunque esperas verdadero porque item.getCategory() es JAZZ. Y podría ser un poco difícil de ver.

Entonces, si en cambio usas el operador == Tendrás 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() 

10
2018-04-21 06:47