Question Comparer les membres de Java enum: == ou equals ()?


Je sais que les enums Java sont compilés en classes avec des constructeurs privés et un groupe de membres statiques publics. En comparant 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 rencontrer un code qui utilise l'opérateur égal == au lieu de .equals ():

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

Quel opérateur est celui que je devrais utiliser?


1418
2017-11-17 17:26


origine


Réponses:


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

j'utilise ==cependant, comme cela sera nullement sûr.


1276
2017-11-17 17:29



Pouvez == être utilisé sur enum?

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

JLS 8.9 Enums

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

C'est une erreur de compilation pour tenter d'instancier explicitement un type enum. le final clone méthode dans Enum s'assure que enum les constantes ne peuvent jamais être clonées, et le traitement spécial du mécanisme de sérialisation garantit que les instances en double ne sont jamais créées à la suite de la désérialisation. L'instanciation réfléchissante des types enum est interdite. Ensemble, ces quatre choses assurent qu'aucun cas d'un enum type existe au-delà de ceux définis par le enum constantes.

Parce qu'il n'y a qu'une seule instance de chaque enum constant, il est permis d'utiliser le == opérateur à la place de la equals méthode lors de la comparaison de deux références d'objet si l'on sait qu'au moins l'un d'entre eux se réfère à un enum constant. (Le equals méthode dans Enum est un final méthode qui invoque simplement super.equals sur son argument et renvoie le résultat, effectuant ainsi une comparaison d'identité.)

Cette garantie est assez forte que Josh Bloch recommande, que si vous insistez sur l'utilisation du pattern singleton, la meilleure façon de l'implémenter est d'utiliser un single-element enum (voir: Effective Java 2nd Edition, élément 3: Appliquer la propriété singleton avec un constructeur privé ou un type enum; aussi Fil de sécurité à Singleton)


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

Pour rappel, il faut dire que généralement, == n'est PAS une alternative viable à equals. Quand il est, cependant (comme avec enum), il y a deux différences importantes à considérer:

== 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 à une vérification de compatibilité de type au moment 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 == être utilisé le cas échéant?

Bloch mentionne spécifiquement que les classes immuables qui ont un contrôle approprié sur leurs instances peuvent garantir à leurs clients que == est utilisable. enum est spécifiquement mentionné pour illustrer.

Point 1: Considérer les méthodes d'usine statiques à la place 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 fait cette garantie, alors ses clients peuvent utiliser le == opérateur au lieu de equals(Object) méthode, ce qui peut entraîner une amélioration des performances. Les types Enum fournissent cette garantie.

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

  • Ça marche.
  • C'est plus rapide.
  • C'est plus sûr à l'exécution.
  • C'est plus sûr à la compilation.

964
2018-05-30 04:38



En utilisant == comparer deux valeurs enum fonctionne parce qu'il n'y a qu'un seul objet pour chaque constante enum.

Sur une note de côté, il n'y a en fait pas besoin d'utiliser == écrire un code null null si vous écrivez votre equals() comme ça:

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

Ceci est une bonne pratique connue sous le nom Comparer les constantes de gauche que vous devez absolument suivre.


72
2017-11-17 17:30



Comme d'autres l'ont dit, les deux == et .equals() travailler dans la plupart des cas. La certitude du temps de compilation que vous ne comparez pas complètement différents types d'objets que d'autres ont signalés est valide et bénéfique, cependant le type particulier de bogue de comparaison d'objets de deux types de temps de compilation différents sera également trouvé par FindBugs (et probablement par Inspections de temps de compilation Eclipse / IntelliJ), de sorte que le compilateur Java trouve que cela n'ajoute pas beaucoup de sécurité supplémentaire.

Toutefois:

  1. Le fait que == ne jette jamais NPE dans mon esprit est un désavantage de ==. Il ne devrait presque jamais être nécessaire enum types à être null, puisque tout état supplémentaire que vous souhaitez exprimer via null peut juste être ajouté à la enum comme une instance supplémentaire. Si c'est inattendu null, Je préfère avoir un NPE que == évaluer silencieusement à faux. Par conséquent, je ne suis pas d'accord avec c'est plus sûr à l'exécution opinion; il est préférable de prendre l'habitude de ne jamais laisser enum les valeurs soient @Nullable.
  2. L'argument selon lequel == est plus rapide est aussi faux. Dans la plupart des cas, vous appelez .equals() sur une variable dont le type de compilation est la classe enum, et dans ce cas le compilateur peut savoir que c'est la même chose que == (parce qu'un enumde equals() méthode ne peut pas être substituée) et peut optimiser l'appel de la fonction. Je ne suis pas sûr si le compilateur le fait actuellement, mais si ce n'est pas le cas, et s'avère être un problème de performance dans l'ensemble de Java, alors je préfère réparer le compilateur que 100 000 programmeurs Java changent leur style de programmation les caractéristiques de performance d'une version particulière du compilateur.
  3. enums sont des objets. Pour tous les autres types d'objets, la comparaison standard est .equals(), ne pas ==. Je pense qu'il est dangereux de faire une exception pour enums parce que vous pourriez finir accidentellement comparer des objets avec == au lieu de equals()surtout si vous refactorisez un enum dans une classe non-enum. Dans le cas d'un tel refactoring, le Ça marche point d'en haut est faux. Pour vous convaincre que l'utilisation de == est correct, vous devez vérifier si la valeur en question est soit un enum ou une primitive; si c'était un nonenumclasse, ce serait faux mais facile à manquer car le code compilerait toujours. Le seul cas où une utilisation de .equals() serait erroné si les valeurs en question étaient primitives; dans ce cas, le code ne compile pas, c'est donc beaucoup plus difficile à manquer. Par conséquent, .equals() est beaucoup plus facile à identifier comme correct, et est plus sûr contre les refactorings futurs.

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 séparé pour l'identité de l'objet, mais ce n'est pas comme cela que Java a été défini.

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


56
2018-02-14 11:59



Voici un test de synchronisation brut 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 une à la fois. Voici les deux compare d'en haut dans le byte-code démonté:

 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 retour booléen de la pile. La seconde (==) compare les adresses d'objets directement à partir de la pile. Dans le premier cas, il y a plus d'activité.

J'ai effectué ce test plusieurs fois avec les deux FI une à la fois. Le "==" est toujours un peu plus rapide.


12
2017-10-14 21:19



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


10
2017-11-17 17:28



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

Une autre raison, en plus des autres déjà discutées ici, est que vous pourriez introduire un bug sans le savoir. Supposons que vous ayez cette énumération qui est exactement la même mais dans des pacakges séparés (ce n'est pas commun, mais cela pourrait arriver):

Première enum:

package first.pckg

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

Deuxième énum:

package second.pckg

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

Alors supposons que vous utilisiez les égaux comme suivant dans item.category lequel est first.pckg.Category mais vous importez la deuxième enum (second.pckg.Category) au lieu de la première sans s'en rendre compte:

import second.pckg.Category;
...

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

Donc, vous aurez toujours false raison est une autre énumération, même si vous attendez vrai parce que item.getCategory() est JAZZ. Et ça pourrait être un peu difficile à voir.

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

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

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

10
2018-04-21 06:47



En utilisant autre chose que == comparer les constantes enum est un non-sens. C'est comme comparant class objets avec equals - Ne le fais pas!

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


4
2017-10-26 20:40



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) pour que == opérateur pourrait être utilisé pour vérifier leur égalité plutôt que d'utiliser equals() méthode


4
2017-11-07 15:21