Frage Vergleichen von Java-Enum-Membern: == oder equals ()?


Ich weiß, dass Java-Enums in Klassen mit privaten Konstruktoren und einer Reihe öffentlicher statischer Member kompiliert werden. Wenn ich zwei Mitglieder eines gegebenen Enums vergleiche, habe ich immer verwendet .equals(), z.B.

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

Ich bin jedoch auf einen Code gestoßen, der den Gleichheitsoperator verwendet == anstelle von .equals ():

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

Welcher Operator sollte ich verwenden?


1418
2017-11-17 17:26


Ursprung


Antworten:


Beide sind technisch korrekt. Wenn Sie sich den Quellcode für .equals()Es wird einfach verschoben ==.

ich benutze ==da dies jedoch null sicher ist.


1276
2017-11-17 17:29



Kann == verwendet werden enum?

Ja: Enums verfügen über strenge Instanzsteuerelemente, die Sie verwenden können == um Instanzen zu vergleichen. Hier ist die Garantie der Sprachspezifikation (Hervorhebung durch mich):

JLS 8.9 Enums

Ein Aufzählungstyp hat keine anderen Instanzen als die, die durch seine Aufzählungskonstanten definiert sind.

Es ist ein Kompilierungsfehler, wenn Sie versuchen, einen Aufzählungstyp explizit zu instanziieren. Das final clone Methode in Enum versichert dass enum Konstanten können niemals geklont werden, und die spezielle Behandlung durch den Serialisierungsmechanismus stellt sicher, dass doppelte Instanzen niemals als Ergebnis der Deserialisierung erstellt werden. Reflektierende Instanziierung von Enum-Typen ist verboten. Zusammen sorgen diese vier Dinge dafür, dass keine Instanzen eines enum Typ existiert über die durch die definiert enum Konstanten.

Weil es nur jeweils eine Instanz gibt enum Konstante, es ist erlaubt, das zu verwenden == Betreiber anstelle der equals Methode beim Vergleich von zwei Objektreferenzen, wenn bekannt ist, dass mindestens einer von ihnen auf ein verweist enum Konstante. (Das equals Methode in Enum ist ein final Methode, die nur aufruft super.equals auf sein Argument und gibt das Ergebnis zurück und führt so einen Identitätsvergleich durch.)

Diese Garantie ist stark genug, dass Josh Bloch empfiehlt, dass, wenn Sie darauf bestehen, das Singleton-Muster zu verwenden, der beste Weg, es zu implementieren, ein einzelnes Element ist enum (sehen: Effektive Java 2nd Edition, Element 3: Erzwingen Sie die Singleton-Eigenschaft mit einem privaten Konstruktor oder einem Enum-Typ; ebenfalls Fadensicherheit in Singleton)


Was sind die Unterschiede zwischen? == und equals?

Zur Erinnerung, es muss allgemein gesagt werden, == ist keine brauchbare Alternative zu equals. Wenn es aber ist (wie mit enum), gibt es zwei wichtige Unterschiede zu beachten:

== nie wirft NullPointerException

enum Color { BLACK, WHITE };

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

== unterliegt der Typkompatibilitätsprüfung zur Kompilierzeit

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!

Sollte == verwendet werden, wenn zutreffend?

Bloch erwähnt ausdrücklich, dass unveränderliche Klassen, die eine angemessene Kontrolle über ihre Instanzen haben, dies ihren Kunden garantieren können == ist verwendbar. enum wird speziell zur Veranschaulichung erwähnt.

Punkt 1: Berücksichtigen Sie statische Factory-Methoden anstelle von Konstruktoren

[...] es ermöglicht einer unveränderlichen Klasse, die Garantie zu geben, dass keine zwei gleichen Instanzen existieren: a.equals(b)dann und nur dann, wenn a==b. Wenn eine Klasse diese Garantie übernimmt, können ihre Kunden die == Betreiber statt der equals(Object) Methode, die zu einer verbesserten Leistung führen kann. Enum-Typen bieten diese Garantie.

Zusammenfassend die Argumente für die Verwendung == auf enum sind:

  • Es klappt.
  • Es ist schneller.
  • Zur Laufzeit ist es sicherer.
  • Es ist sicherer zur Kompilierzeit.

964
2018-05-30 04:38



Verwenden == Der Vergleich zweier Aufzählungswerte funktioniert, da für jede Aufzählungskonstante nur ein Objekt vorhanden ist.

Nebenbei bemerkt, gibt es eigentlich keine Notwendigkeit zu verwenden == um null sicheren Code zu schreiben, wenn Sie Ihr schreiben equals() so was:

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

Dies ist eine bewährte Methode Vergleichen Sie die Konstanten von links Dem solltest du unbedingt folgen.


72
2017-11-17 17:30



Wie andere gesagt haben, beides == und .equals() in den meisten Fällen arbeiten. Die Kompilierzeitgewissheit, dass Sie nicht völlig verschiedene Arten von Objekten vergleichen, auf die andere hingewiesen haben, ist gültig und nützlich, jedoch würde die spezielle Art des Fehlers beim Vergleich von Objekten zweier verschiedener Kompilierzeittypen auch von FindBugs (und wahrscheinlich von Eclipse / IntelliJ Kompilierzeit Inspektionen), so dass der Java-Compiler, der es findet, nicht so viel zusätzliche Sicherheit hinzufügt.

Jedoch:

  1. Die Tatsache, dass == nie wirft NPE in meinen Gedanken ist ein Nachteil von ==. Es sollte kaum nötig sein enum Typen sein null, da es sich um einen zusätzlichen Status handelt, über den Sie möglicherweise per Express ausdrücken möchten null kann nur hinzugefügt werden enum als zusätzliche Instanz. Wenn es unerwartet ist nullIch hätte lieber eine NPE als == im Stillen zu falsch bewerten. Deshalb stimme ich dem nicht zu es ist zur Laufzeit sicherer Meinung; Es ist besser, sich angewöhnen zu lassen enum Werte sein @Nullable.
  2. Das Argument, dass == ist schneller ist auch falsch. In den meisten Fällen werden Sie anrufen .equals() für eine Variable, deren Kompilierzeittyp die Enum-Klasse ist, und in diesen Fällen kann der Compiler wissen, dass dies derselbe ist == (weil ein enumist es equals() Methode kann nicht überschrieben werden) und kann den Funktionsaufruf optimieren. Ich bin mir nicht sicher, ob der Compiler dies jetzt tut, aber wenn nicht und stellt sich heraus, dass es insgesamt ein Performance-Problem in Java ist, dann repariere ich lieber den Compiler, als dass 100.000 Java-Programmierer ihren Programmierstil ändern die Leistungsmerkmale einer bestimmten Compiler-Version.
  3. enums sind Objekte. Für alle anderen Objekttypen ist der Standardvergleich .equals()nicht ==. Ich halte es für gefährlich, eine Ausnahme zu machen enums weil Sie am Ende möglicherweise versehentlich mit Objekten vergleichen == Anstatt von equals()vor allem, wenn Sie ein enum in eine Nicht-Enum-Klasse. Im Falle eines solchen Refactorings, der Es klappt Punkt von oben ist falsch. Um sich davon zu überzeugen, dass eine Verwendung von == ist richtig, Sie müssen überprüfen, ob der Wert in Frage entweder ein ist enum oder ein Primitiv; wenn es ein nichtenumKlasse, es wäre falsch, aber leicht zu verpassen, weil der Code noch kompilieren würde. Der einzige Fall, wenn eine Verwendung von .equals() wäre falsch, wenn die fraglichen Werte primitiv wären; In diesem Fall würde der Code nicht kompiliert werden, so dass es viel schwerer zu übersehen ist. Daher, .equals() ist viel einfacher zu identifizieren als korrekt und ist sicherer gegen zukünftige Refactorings.

Ich glaube tatsächlich, dass die Java-Sprache == auf Objekten definiert haben sollte, um .equals () auf dem linken Wert zu nennen und einen separaten Operator für die Objektidentität einzuführen, aber so wurde Java nicht definiert.

Zusammenfassend denke ich, dass die Argumente für die Verwendung sprechen .equals() zum enum Arten.


56
2018-02-14 11:59



Hier ist ein grober Timing-Test, um die beiden zu vergleichen:

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

}

Kommentieren Sie die IF einzeln aus. Hier sind die zwei Vergleiche von oben in disassembliertem Byte-Code:

 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

Der erste (equals) führt einen virtuellen Aufruf durch und testet den Rückgabe-Boolean vom Stapel. Die zweite (==) vergleicht die Objektadressen direkt vom Stapel. Im ersten Fall gibt es mehr Aktivität.

Ich habe diesen Test mehrmals mit beiden IFs nacheinander durchgeführt. Das "==" ist immer etwas schneller.


12
2017-10-14 21:19



Im Fall von enum sind beide richtig und richtig !!


10
2017-11-17 17:28



Ich bevorzuge es zu benutzen == Anstatt von equals:

Ein anderer Grund, zusätzlich zu den anderen, die bereits hier besprochen wurden, ist, dass Sie einen Fehler einführen könnten, ohne es zu merken. Angenommen, Sie haben diese Enums, die genau die gleichen sind, aber in getrennten Pacakges (es ist nicht üblich, aber es könnte passieren):

Erstes Enum:

package first.pckg

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

Zweite Enum:

package second.pckg

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

Dann nehmen Sie an, dass Sie die Gleichen wie in nächsten verwenden item.category welches ist first.pckg.Category aber du importierst die zweite enum (second.pckg.Category) statt der erste ohne es zu merken:

import second.pckg.Category;
...

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

So wirst du immer kommen false fällig ist ein anderes enum, obwohl du wahr erwartest, weil item.getCategory() ist JAZZ. Und es könnte ein bisschen schwierig sein zu sehen.

Also, wenn Sie stattdessen den Operator verwenden == Sie werden einen Kompilierungsfehler haben:

operator == kann nicht auf "second.pckg.Category", "first.pckg.Category" angewendet werden

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

10
2018-04-21 06:47