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?
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.
==
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
dansEnum
garantit que les constantesenum
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 typeenum
n'existe au-delà de celles définies par les constantesenum
.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éthodeequals
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 constanteenum
. (La méthodeequals
dansEnum
est une méthodefinal
qui invoque simplementsuper.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 )
==
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 compilationenum 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 sia==b
. Si une classe offre cette garantie, ses clients peuvent utiliser l'opérateur==
à la place de la méthodeequals(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:
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.
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:
==
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
.==
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.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
.
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()
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.
En cas d'énumération, les deux sont corrects et corrects !!
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.
Une autre option est la méthode d’utilitaire Objects.equals
.
_Objects.equals( thisEnum , thatEnum )
_
Objects.equals
_ pour null-safetyest é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 Objects
ajoutée à Java 7 et versions ultérieures.
Voici un exemple utilisant l’énumération Month
.
_boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ; // Returns `false`.
_
Je trouve quelques avantages à cette méthode:
true
false
NullPointerException
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));
_
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.
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.)
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.