Est-il OK d'utiliser ==
Sur des énumérations en Java, ou dois-je utiliser .equals()
? Lors de mes tests, ==
Fonctionne toujours, mais je ne sais pas si j'en ai la garantie. En particulier, il n'y a pas de méthode .clone()
sur une énumération, donc je ne sais pas s'il est possible d'obtenir une énumération pour laquelle .equals()
renverrait une valeur différente de ==
.
Par exemple, est-ce OK:
public int round(RoundingMode roundingMode) {
if(roundingMode == RoundingMode.HALF_UP) {
//do something
} else if (roundingMode == RoundingMode.HALF_EVEN) {
//do something
}
//etc
}
Ou dois-je l'écrire de cette façon:
public int round(RoundingMode roundingMode) {
if(roundingMode.equals(RoundingMode.HALF_UP)) {
//do something
} else if (roundingMode.equals(RoundingMode.HALF_EVEN)) {
//do something
}
//etc
}
Juste mes 2 cents: voici le code pour Enum.Java, tel que publié par Sun, et une partie du 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;
}
}
Oui, == est très bien - il n'y a garanti qu'une seule référence pour chaque valeur.
Cependant, il existe une meilleure façon d'écrire votre méthode ronde:
public int round(RoundingMode roundingMode) {
switch (roundingMode) {
case HALF_UP:
//do something
break;
case HALF_EVEN:
//do something
break;
// etc
}
}
Une manière encore meilleure de le faire est de mettre la fonctionnalité dans l'énumération elle-même, de sorte que vous pouvez simplement appeler roundingMode.round(someValue)
. Cela va au cœur des énumérations Java - elles sont des énumérations orientées objet , contrairement aux "valeurs nommées" trouvé ailleurs.
EDIT: La spécification n'est pas très claire, mais section 8.9 déclare:
Le corps d'un type enum peut contenir des constantes enum. Une constante enum définit une instance du type enum. Un type enum n'a pas d'instances autres que celles définies par ses constantes enum.
Oui, c'est comme si vous aviez créé des instances singleton pour chaque valeur de l'énumération:
classe abstraite publique RoundingMode { public statique final RoundingMode HALF_UP = nouveau RoundingMode (); public statique final RoundingMode HALF_EVEN = nouveau RoundingMode (); private RoundingMode () { // la portée private empêche tout sous-type en dehors de cette classe } }
Toutefois, la construction enum
vous offre divers avantages:
switch-case
structure de contrôle.values
qui est "généré" pour chaque type d'énumérationLa sérialisation est un gros gotchya. Si je devais utiliser le code ci-dessus au lieu d'une énumération, voici comment se comporterait l'égalité d'identité:
RoundingMode original = RoundingMode.HALF_UP; Assert (RoundingMode.HALF_UP == original); // passe 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); // échoue assert (RoundingMode.HALF_EVEN == désérialisé); // échoue
Toi pouvez résoudre ce problème sans énumération, en utilisant une technique qui implique writeReplace
et readResolve
, (voir http://Java.Sun.com/j2se/1.4.2/docs/api /Java/io/Serializable.html ) ...
Je suppose que le point est - Java fait tout son possible pour vous permettre d'utiliser les identités des valeurs d'énumération pour tester l'égalité; c'est une pratique encouragée.
== compare les références de deux objets. Pour les énumérations, il est garanti qu'il n'y aura qu'une seule instance, et donc pour deux énumérations identiques, == sera vrai.
Référence:
http://www.ajaxonomy.com/2007/Java/making-the-most-of-Java-50-enum-tricks
(impossible de trouver quoi que ce soit dans les documents Sun)
Voici un code maléfique que vous pourriez trouver intéressant. :RÉ
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));
}
Les énumérations sont un excellent endroit pour brouiller le code polymorphe.
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);
}
Notez qu'il y a un problème lors du transfert d'énumération via RMI/IIOP. Voir ce fil:
http://www.velocityreviews.com/forums/t390342-enum-equality.html
== est généralement correct, et il y a des avantages à la fois == et .equals()
. Personnellement, je préfère toujours utiliser .equals()
lors de la comparaison d'objets, y compris enum
s. Voir aussi cette discussion: