2009-02-10 22 views
85

¿Está bien utilizar == en enumeraciones en Java, o necesito usar .equals()? En mi prueba, == siempre funciona, pero no estoy seguro de si estoy seguro de eso. En particular, no hay un método .clone() en una enumeración, por lo que no sé si es posible obtener una enumeración para la cual .equals() devolvería un valor diferente de ==.¿Está bien usar == en enumeraciones en Java?

Por ejemplo, ¿Está bien:

public int round(RoundingMode roundingMode) { 
    if(roundingMode == RoundingMode.HALF_UP) { 
    //do something 
    } else if (roundingMode == RoundingMode.HALF_EVEN) { 
    //do something 
    } 
    //etc 
} 

o tengo que escribir de esta manera:

public int round(RoundingMode roundingMode) { 
    if(roundingMode.equals(RoundingMode.HALF_UP)) { 
    //do something 
    } else if (roundingMode.equals(RoundingMode.HALF_EVEN)) { 
    //do something 
    } 
    //etc 
} 
+4

posible duplicado de [Comparando los miembros de enumeración de Java:? == o iguales()] (http://stackoverflow.com/questions/1750435/comparing-java-enum-members -o iguales) – assylias

+0

@assylias esta pregunta fue lo primero. Quizás sea una bandera para ♦ atención, ya que no estoy seguro de si los dos deberían fusionarse. –

+0

@MattBall Creo que la respuesta a su pregunta que cita el JLS es la mejor respuesta, y es por eso que elegí cerrar esta. – assylias

Respuesta

117

Sólo mi 2 centavos: Aquí está el código para Enum.java, según lo publicado por Sun, y parte del JDK:

public abstract class Enum<E extends Enum<E>> 
    implements Comparable<E>, Serializable { 

    // [...] 

    /** 
    * Returns true if the specified object is equal to this 
    * enum constant. 
    * 
    * @param other the object to be compared for equality with this object. 
    * @return true if the specified object is equal to this 
    *   enum constant. 
    */ 
    public final boolean equals(Object other) { 
     return this==other; 
    } 


} 
+1

¡Gracias! Supongo que si hubiera pensado en entrar en .equals() con el compilador, lo habría visto ... – Kip

70

Sí, == está bien - no garantiza que sea sólo una sola referencia para cada valor

Sin embargo, hay una mejor manera de escribir su método de ronda:

public int round(RoundingMode roundingMode) { 
    switch (roundingMode) { 
    case HALF_UP: 
     //do something 
     break; 
    case HALF_EVEN: 
     //do something 
     break; 
    // etc 
    } 
} 

Un aun mejor forma de hacerlo es poner la funcionalidad dentro de la propia enumeración, por lo que sólo podría llamar roundingMode.round(someValue). Esto llega al corazón de las enumeraciones de Java: son enum orientadas a objetos, a diferencia de los "valores con nombre" que se encuentran en otros lugares.

EDIT: La especificación no es muy clara, pero section 8.9 estados:

El cuerpo de un tipo de enumeración pueden contener constantes de enumeración. Una constante enum define una instancia del tipo enum. Un tipo de enumeración no tiene otras instancias que las definidas por sus constantes enum .

+0

Me encantaría tomar tu palabra para ello, pero si pudieras proporcionar un enlace a alguna documentación oficial que sería mejor ... – Kip

+0

Buscará el bit relevante de la especificación o documentos de Java. –

+0

¿Está bien? ¡Ese es todo el punto! El interruptor –

6

Aquí hay algunos códigos malvados que pueden resultarle interesantes. : D

public enum YesNo {YES, NO} 

public static void main(String... args) throws Exception { 
    Field field = Unsafe.class.getDeclaredField("theUnsafe"); 
    field.setAccessible(true); 
    Unsafe unsafe = (Unsafe) field.get(null); 
    YesNo yesNo = (YesNo) unsafe.allocateInstance(YesNo.class); 

    Field name = Enum.class.getDeclaredField("name"); 
    name.setAccessible(true); 
    name.set(yesNo, "YES"); 

    Field ordinal = Enum.class.getDeclaredField("ordinal"); 
    ordinal.setAccessible(true); 
    ordinal.set(yesNo, 0); 

    System.out.println("yesNo " + yesNo); 
    System.out.println("YesNo.YES.name().equals(yesNo.name()) "+YesNo.YES.name().equals(yesNo.name())); 
    System.out.println("YesNo.YES.ordinal() == yesNo.ordinal() "+(YesNo.YES.ordinal() == yesNo.ordinal())); 
    System.out.println("YesNo.YES.equals(yesNo) "+YesNo.YES.equals(yesNo)); 
    System.out.println("YesNo.YES == yesNo " + (YesNo.YES == yesNo)); 
} 
+1

¡no seas malvado! Por cierto, puede hacer algo similar con las cadenas que están internados ... – Chii

11

Sí, es como si hubiera creado casos únicos para cada valor de la enumeración:

 
public abstract class RoundingMode { 
    public static final RoundingMode HALF_UP = new RoundingMode(); 
    public static final RoundingMode HALF_EVEN = new RoundingMode(); 

    private RoundingMode() { 
    // private scope prevents any subtypes outside of this class 
    } 
} 

Sin embargo, el constructo enum le ofrece diversos beneficios:

  • Cada instancia de toString() imprime el nombre dado en el código.
  • (Como se menciona en otra publicación,) una variable del tipo enum se puede comparar con constantes usando la estructura de control switch-case.
  • Todos los valores de la enumeración se pueden consultar en el campo values que se 'generó' para cada tipo de enumeración
  • aquí está la gran comparaciones de identidad de uno w.r.t: valores de enumeración sobreviven serialización sin clonación.

La serialización es una gran gotchya. Si tuviera que usar el código anterior en lugar de una enumeración, así es como se comportaría la igualdad identidad:

 
RoundingMode original = RoundingMode.HALF_UP; 
assert (RoundingMode.HALF_UP == original); // passes 

ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
ObjectOutputStream oos = new ObjectOutputStream(baos); 
oos.writeObject(original); 
oos.flush(); 

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 
ObjectInputStream ois = new ObjectInputStream(bais); 
RoundingMode deserialized = (RoundingMode) ois.readObject(); 

assert (RoundingMode.HALF_UP == deserialized); // fails 
assert (RoundingMode.HALF_EVEN == deserialized); // fails 

Usted puede a resolver este problema sin enumeración, utilizando una técnica que consiste en writeReplace y readResolve, (ver http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html) ...

Supongo que el punto es que Java hace todo lo posible para permitirle utilizar las identidades de los valores enum para probar la igualdad; es una práctica recomendada.

+1

+1 Excelentes notas sobre la serialización –

+1

Se solucionó el error de serialización. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id = 6277781 –

+0

@DavidI. gracias por la actualización. Es un error muy perturbador, ¡y es bueno saberlo! –

3

Las enumeraciones son un gran lugar para introducir código polimórfico.

enum Rounding { 
    ROUND_UP { 
    public int round(double n) { ...; } 
    }, 
    ROUND_DOWN { 
    public int round(double n) { ...; } 
    }; 

    public abstract int round(double n); 
} 

int foo(Rounding roundMethod) { 
    return roundMethod.round(someCalculation()); 
} 

int bar() { 
    return foo(Rounding.ROUND_UP); 
} 
+1

Sí, pero no soy dueño de java.math.RoundingMode, así que no puedo hacer esto en mi caso. – Kip

Cuestiones relacionadas