2009-02-10 9 views
85

Ist es in Ordnung, == auf Enums in Java zu verwenden, oder muss ich .equals() verwenden? In meinem Test funktioniert == immer, aber ich bin mir nicht sicher, ob ich dafür garantiert bin. Insbesondere gibt es keine .clone() Methode für eine Enumeration, daher weiß ich nicht, ob es möglich ist, eine Enumeration zu erhalten, für die .equals() einen anderen Wert als == zurückgeben würde.Ist es OK == in Enums in Java zu verwenden?

Zum Beispiel ist das in Ordnung:

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

Oder muss ich es auf diese Weise schreiben:

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

möglich Duplikat [Vergleich Java Enum Mitglieder: == oder equals()] (http://stackoverflow.com/questions/1750435/comparing-java-enum-members -oder-gleich) – assylias

+0

@assylias diese Frage kam zuerst. Vielleicht Flagge für ♦ Aufmerksamkeit, da ich nicht wirklich sicher bin, ob die beiden zusammengeführt werden sollen. –

+0

@MattBall Ich denke, die Antwort auf Ihre Frage, die die JLS zitiert, ist die beste Antwort, weshalb ich beschloss, diese zu schließen. – assylias

Antwort

117

nur 2 Cent (alles in der Sun-Dokumentation nicht finden konnte): Hier ist der Code für Enum.java, wie von Sun veröffentlicht, und Teil des 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

Danke! Ich denke, wenn ich nur gedacht hätte, in .equals() mit dem Compiler zu treten, hätte ich das gesehen ... – Kip

70

Ja, == ist in Ordnung - es ist garantiert nur eine einzige Referenz sein für jeden Wert.

Allerdings gibt es einen besseren Weg, um Ihre Runde Verfahren zum Schreiben:

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

Eine noch besser Art und Weise, es zu tun ist, um die Funktionalität innerhalb des ENUM selbst zu setzen, so dass Sie nur roundingMode.round(someValue) nennen könnte. Das kommt auf das Herz von Java enums - sie sind objektorientierte enums, anders als die "benannten Werte" an anderer Stelle gefunden.

EDIT: Die Spezifikation ist nicht ganz klar, aber section 8.9 heißt es:

Der Körper eines Aufzählungstyp Enum-Konstanten enthalten. Eine Enum-Konstante definiert eine Instanz des Enum-Typs. Ein Enum-Typ hat keine anderen Instanzen als diejenigen, die durch seine enum Konstanten definiert sind.

+0

Ich würde gerne Ihr Wort dafür nehmen, aber wenn Sie einen Link zu einigen offiziellen Dokumentation, die besser sein könnte ... – Kip

+0

wird nach dem relevanten Bit der Spezifikation oder Java-Dokumente suchen. –

+0

Ist es in Ordnung? Das ist der springende Punkt! –

6

Hier ist etwas bösartiger Code, den Sie vielleicht interessant finden könnten. : 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

sei nicht böse! BTW, können Sie eine ähnliche Sache mit Strings, die interniert sind ... – Chii

11

Ja, ist es, als ob Sie Singleton-Instanzen für jeden Wert in der Enumeration erstellt hatte:

 
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 
    } 
} 

jedoch, das enum Konstrukt gibt Ihnen verschiedene Vorteile:

  • Der toString() der Instanz gibt den im Code angegebenen Namen aus.
  • (Wie in einem anderen Beitrag erwähnt,) eine Variable des Enum-Typs kann gegen Konstanten mit der switch-case Kontrollstruktur verglichen werden.
  • Alle Werte in der Aufzählung können mit dem values Feld abgefragt werden, die ‚‘ erzeugt wird für jeden Aufzählungstyp
  • Hier die großen w.r.t Identitätsvergleiche ist: ENUM-Werte Serialisierung ohne Klonen überleben.

Die Serialisierung ist ein großer Gotchya. Wenn ich den Code oben anstelle eines Enum verwenden waren, ist hier, wie Identität Gleichheit verhalten würde:

 
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 

Sie können Adresse dieses Problem ohne Enum, eine Technik, die writeReplace und readResolve beinhaltet (siehe http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html) ...

Ich denke, der Punkt ist - Java geht aus dem Weg, damit Sie die Identitäten von Enum-Werten zum Testen der Gleichheit verwenden können; Es ist eine ermutigende Praxis.

+1

+1 Great Notes zur Serialisierung –

+1

Serialisierung Bug wurde behoben. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id = 6277781 –

+0

@DavidI. Danke für das Update. Das ist ein sehr beunruhigender Bug und gut zu wissen! –

3

Enums sind ein großartiger Ort, um polymorphen Code zu blockieren.

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

Ja, aber ich habe java.math.RoundingMode nicht, also kann ich das in meinem Fall nicht tun. – Kip

1

== im Allgemeinen in Ordnung ist, und es gibt Vorteile sowohl == und .equals(). Ich persönlich bevorzuge immer .equals() beim Vergleich von Objekten, einschließlich enum s. Siehe auch diese Diskussion:

Comparing Java enum members: == or equals()?

Verwandte Themen