web-dev-qa-db-fra.com

Est-il correct d'utiliser == sur les énumérations en Java?

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

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


}
145
Varkhan

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.

76
Jon Skeet

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:

  • La toString () de chaque instance affiche le nom donné dans le code.
  • (Comme mentionné dans un autre article,) une variable du type enum peut être comparée à des constantes en utilisant le switch-case structure de contrôle.
  • Toutes les valeurs de l'énumération peuvent être interrogées à l'aide du champ values qui est "généré" pour chaque type d'énumération
  • Voici les comparaisons d'identité les plus importantes avec: les valeurs enum survivent à la sérialisation sans clonage.

La 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.

13
Dilum Ranatunga

== 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)

10
levand

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));
}
6
Peter Lawrey

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);
}
3
paulmurray

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

1
johny

== 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 enums. Voir aussi cette discussion:

Comparaison Java membres enum: == ou equals ()?

1
Tobias