web-dev-qa-db-fra.com

Comparaison de Java membres enum: == ou est égal à ()?

Je sais que Java les énumérations sont compilées en classes avec des constructeurs privés et un groupe de membres statiques publics. Lors de la comparaison de deux membres d'une énumération donnée, j'ai toujours utilisé .equals(), par exemple.

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

Cependant, je viens de tomber sur un code qui utilise l'opérateur d'égalité == au lieu de .equals ():

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

Quel opérateur est celui que je devrais utiliser?

1594
Matt Ball

Les deux sont techniquement corrects. Si vous regardez le code source pour .equals(), il se contente de passer à ==.

J'utilise ==, cependant, car ce sera null safe.

1436
Reverend Gonzo

Est-ce que == peut être utilisé sur enum?

Oui: les enums ont des contrôles d'instance serrés qui vous permettent d'utiliser == pour comparer des instances. Voici la garantie fournie par la spécification de langue (souligné par moi):

JLS 8.9 Enums

Un type enum n'a pas d'autres instances que celles définies par ses constantes enum.

Tenter d'instancier explicitement un type enum est une erreur de compilation. La méthode final clone dans Enum garantit que les constantes enum ne peuvent jamais être clonées, et le traitement spécial par le mécanisme de sérialisation garantit que les instances dupliquées ne sont jamais créées à la suite de la désérialisation. L'instanciation réflexive des types enum est interdite. Ensemble, ces quatre éléments garantissent qu'aucune instance d'un type enum n'existe au-delà de celles définies par les constantes enum.

Comme il n'y a qu'une seule instance de chaque constante enum, , il est permis d'utiliser l'opérateur == à la place de la méthode equals lors de la comparaison de deux objets références si on sait qu'au moins l'une d'elles fait référence à une constante enum. (La méthode equals dans Enum est une méthode final qui invoque simplement super.equals sur son argument et renvoie le résultat, ce qui permet d'effectuer une comparaison d'identité.)

Cette garantie est suffisamment solide, comme le recommande Josh Bloch, si vous insistez pour utiliser le modèle singleton, le meilleur moyen de le mettre en œuvre est d'utiliser un seul élément enum (voir: Effective Java 2ème édition, élément 3: Appliquez la propriété singleton avec un constructeur privé ou un type enum ; aussi sécurité du thread dans Singleton )


Quelles sont les différences entre == et equals?

Pour rappel, il convient de préciser qu'en général, == n'est PAS une alternative viable à equals. Dans ce cas, cependant (comme avec enum), il y a deux différences importantes à prendre en compte:

== ne jette jamais NullPointerException

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

== est soumis au contrôle de compatibilité de type lors de la compilation

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!

== devrait-il être utilisé, le cas échéant?

Bloch mentionne spécifiquement que les classes immuables qui contrôlent correctement leurs instances peuvent garantir à leurs clients que == est utilisable. enum est spécifiquement mentionné pour illustrer.

Point 1: Considérez les méthodes fabriques statiques au lieu des constructeurs

[...] il permet à une classe immuable de garantir qu'il n'existe pas deux instances égales: a.equals(b) si et seulement si a==b. Si une classe offre cette garantie, ses clients peuvent utiliser l'opérateur == à la place de la méthode equals(Object), ce qui peut améliorer les performances. Les types Enum fournissent cette garantie.

Pour résumer, les arguments pour utiliser == sur enum sont les suivants:

  • Ça marche.
  • C'est plus rapide.
  • C'est plus sûr au moment de l'exécution.
  • C'est plus sûr au moment de la compilation.
1040

L'utilisation de == pour comparer deux valeurs enum fonctionne, car il n'y a qu'un seul objet pour chaque constante enum.

Sur une note de côté, il n’est en fait pas nécessaire d’utiliser == pour écrire un code sécurisé nul si vous écrivez votre equals() comme ceci:

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

Il s'agit d'une meilleure pratique connue sous le nom de Comparer les constantes de la gauche que vous devriez absolument suivre.

77
Pascal Thivent

Comme d'autres l'ont dit, == et .equals() fonctionnent tous les deux dans la plupart des cas. La certitude du temps de compilation que vous ne comparez pas complètement des types d'objets différents signalés par d'autres est valide et utile. Cependant, le type de bogue particulier de comparaison d'objets de deux types de temps de compilation différents serait également détecté par FindBugs (et probablement par Inspections de temps de compilation Eclipse/IntelliJ), donc le compilateur Java le trouvant ne ajoutant pas beaucoup de sécurité supplémentaire.

Pourtant:

  1. Le fait que == ne jette jamais NPE dans mon esprit est un inconvénient de ==. Il ne devrait pratiquement jamais être nécessaire que les types enum soient null, car tout état supplémentaire que vous souhaitez exprimer via null peut simplement être ajouté à la variable enum une instance supplémentaire. Si c'est inattendu null, je préférerais avoir un NPE plutôt que == évaluant silencieusement à false. Par conséquent, je ne suis pas d'accord avec le , c'est plus sûr au moment de l'exécution opinion; il vaut mieux prendre l'habitude de ne jamais laisser les valeurs enum être @Nullable.
  2. L'argument selon lequel == est plus rapide est également faux. Dans la plupart des cas, vous appelerez .equals() sur une variable dont le type de compilation est la classe enum. Dans ce cas, le compilateur peut savoir qu'il s'agit de la même chose que == (car enum La méthode equals() ne peut pas être ignorée et peut optimiser l'appel de fonction. Je ne sais pas si le compilateur le fait actuellement, mais dans le cas contraire, et si cela pose un problème de performances dans Java, je préférerais réparer le compilateur que d'avoir 100 000 Java les programmeurs modifient leur style de programmation en fonction des caractéristiques de performance d'une version du compilateur particulière.
  3. enums sont des objets. Pour tous les autres types d'objet, la comparaison standard est .equals() et non ==. Je pense qu'il est dangereux de faire une exception pour enums parce que vous pourriez finir par comparer accidentellement des objets avec == au lieu de equals(), surtout si vous refactorisez un enum en non classe enum. Dans le cas d'une telle refactorisation, le point Cela fonctionne d'en haut est incorrect. Pour vous convaincre que l'utilisation de == est correcte, vous devez vérifier si la valeur en question est une enum ou une primitive; s'il s'agissait d'une classe non -enum, il serait faux mais facile à rater car le code serait toujours compilé. Le seul cas où une utilisation de .equals() serait erronée est si les valeurs en question étaient des primitives; dans ce cas, le code ne compilerait pas, il est donc beaucoup plus difficile à manquer. Par conséquent, .equals() est beaucoup plus facile à identifier comme correct et est plus sûr contre les refactorisations futures.

Je pense en fait que le langage Java aurait dû définir == sur les objets pour appeler .equals () sur la valeur de gauche et introduire un opérateur distinct pour l'identité de l'objet, mais ce n'est pas ainsi que Java était défini.

En résumé, je pense toujours que les arguments sont en faveur de l'utilisation de .equals() pour les types enum.

59
Tobias

Je préfère utiliser == au lieu de equals:

Une autre raison, en plus des autres déjà discutés ici, est que vous pourriez introduire un bogue sans vous en rendre compte. Supposons que vous ayez cette énumération qui est exactement la même mais dans des paquets séparés (ce n'est pas courant, mais cela peut arriver):

premier enum:

package first.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Deuxième enum:

package second.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Supposons alors que vous utilisez les égaux comme suit dans item.category qui est first.pckg.Category mais vous importez le deuxième enum (second.pckg.Category) à la place du premier sans vous en rendre compte:

import second.pckg.Category;
...

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

Ainsi, vous obtiendrez toujours false parce que l'énumération est différente, bien que vous vous attendiez à la vérité, car item.getCategory() est JAZZ. Et cela pourrait être un peu difficile à voir.

Donc, si vous utilisez plutôt l'opérateur ==, vous aurez une erreur de compilation:

l'opérateur == ne peut pas être appliqué à "second.pckg.Category", "first.pckg.Category"

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 
15
Pau

Voici un test de synchronisation grossier pour comparer les deux:

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

}

Commentez les FI les uns après les autres. Voici les deux comparaisons ci-dessus dans le code octet désassemblé:

 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

Le premier (égal) effectue un appel virtuel et teste le booléen renvoyé à partir de la pile. Le second (==) compare les adresses d'objet directement à partir de la pile. Dans le premier cas, il y a plus d'activité.

J'ai exécuté ce test plusieurs fois avec les deux FI, un à la fois. Le "==" est légèrement plus rapide.

13
ChrisCantrell

En cas d'énumération, les deux sont corrects et corrects !!

9
Suraj Chandran

Utiliser autre chose que == pour comparer les constantes enum n'a pas de sens. C'est comme en comparant les objets class avec equals - ne le faites pas!

Cependant, il y avait un bogue méchant (BugId 6277781 ) dans Sun JDK 6u10 et versions antérieures qui pourrait être intéressant pour des raisons historiques. Ce bogue empêchait l'utilisation correcte de == sur les enumérations désérialisées, bien que ce soit sans doute un peu le cas.

6
Tomas

tl; dr

Une autre option est la méthode d’utilitaire Objects.equals .

_Objects.equals( thisEnum , thatEnum )
_

_Objects.equals_ pour null-safety

est égal à opérateur == au lieu de .equals ()

Quel opérateur est celui que je devrais utiliser?

Une troisième option est la méthode statique equals trouvée dans la classe d’utilitaires Objectsajoutée à Java 7 et versions ultérieures.

Exemple

Voici un exemple utilisant l’énumération Month .

_boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ;  // Returns `false`.
_

Avantages

Je trouve quelques avantages à cette méthode:

  • Null-safety
  • Compact, lisible

Comment ça fonctionne

Quelle est la logique utilisée par Objects.equals ?

Voyez vous-même, à partir du code source Java 10 de OpenJDK :

_return (a == b) || (a != null && a.equals(b));
_
5
Basil Bourque

Les énumérations sont des classes qui renvoient une instance (comme des singletons) pour chaque constante d'énumération déclarée par public static final field (immuable), de sorte que l'opérateur == puisse être utilisé pour vérifier leur égalité plutôt que d'utiliser la méthode equals().

La raison pour laquelle enums fonctionne facilement avec == est que chaque instance définie est également un singleton. Ainsi, la comparaison d’identité à l’aide de == fonctionnera toujours.

Mais utiliser == parce que cela fonctionne avec des énumérations signifie que tout votre code est étroitement associé à l’utilisation de cette énumération.

Par exemple: Enums peut implémenter une interface. Supposons que vous utilisiez actuellement une énumération qui implémente Interface1. Si plus tard, quelqu'un le modifie ou introduit une nouvelle classe Impl1 en tant qu'implémentation de la même interface. Ensuite, si vous commencez à utiliser des instances de Impl1, vous aurez beaucoup de code à modifier et à tester en raison de l'utilisation antérieure de ==.

Par conséquent, il est préférable de suivre ce qui est considéré comme une bonne pratique à moins d’un gain justifiable.

2
Dev Amitabh

En bref, les deux ont des avantages et des inconvénients.

D'une part, il est avantageux d'utiliser ==, comme décrit dans les autres réponses.

D'autre part, si pour quelque raison que ce soit, vous remplacez les enums par une approche différente (instances de classe normales), après avoir utilisé == vous mord. (BTDT.)

1
glglgl

Je souhaite compléter les polygenelubricants.

Personnellement, je préfère equals (). Mais il faut vérifier la compatibilité de type. Ce que je pense est une limitation importante.

Pour que la compatibilité de type soit vérifiée au moment de la compilation, déclarez et utilisez une fonction personnalisée dans votre enum.

public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable

Avec cela, vous bénéficiez de tous les avantages des deux solutions: protection NPE, lecture facile du code et vérification de la compatibilité des types au moment de la compilation.

Je recommande également d'ajouter une valeur UNDEFINED pour enum.

0
Christ