Was ist der Vorteil einer Java-Enumeration gegenüber einer Klasse mit öffentlichen statischen Endfeldern?


147

Ich bin sehr vertraut mit C #, fange aber an, mehr in Java zu arbeiten. Ich hatte erwartet zu erfahren, dass Enums in Java im Grunde denen in C # entsprechen, aber anscheinend ist dies nicht der Fall. Anfangs war ich begeistert zu erfahren, dass Java-Enums mehrere Daten enthalten können, was sehr vorteilhaft erscheint ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Seitdem fehlen jedoch viele Funktionen, die in C # trivial sind, z. B. die Möglichkeit, einem Aufzählungselement auf einfache Weise einen bestimmten Wert zuzuweisen, und folglich die Möglichkeit, eine Ganzzahl ohne großen Aufwand in eine Aufzählung umzuwandeln ( dh Konvertieren Sie einen ganzzahligen Wert in eine übereinstimmende Java-Aufzählung .

Meine Frage lautet also: Gibt es einen Vorteil für Java-Aufzählungen gegenüber einer Klasse mit einer Reihe öffentlicher statischer Endfelder? Oder bietet es nur eine kompaktere Syntax?

EDIT: Lassen Sie mich klarer sein. Was ist der Vorteil von Java-Aufzählungen gegenüber einer Klasse mit einer Reihe öffentlicher statischer Endfelder desselben Typs ? Was ist beispielsweise im Planetenbeispiel unter dem ersten Link der Vorteil einer Aufzählung gegenüber einer Klasse mit diesen öffentlichen Konstanten:

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);

Soweit ich das beurteilen kann, ist die Antwort von casablanca die einzige, die dies befriedigt.


4
@Bohemian: Es darf kein Duplikat sein, da das OP nur public static finalFelder erwähnte , bei denen es sich möglicherweise um typisierte Werte handelt und nicht unbedingt um ints.
Casablanca

1
@ Shahzeb Kaum. Die Verwendung von Enums anstelle von String-Konstanten ist eine EXTREM gute Idee und wird mehr als empfohlen. Typensicherheit, benötigt keine statischen Dienstprogrammfunktionen und so weiter. Absolut kein Grund, stattdessen Zeichenfolgen zu verwenden.
Voo

1
@Voo Ja, ich wusste, dass es Meinungsverschiedenheiten geben wird. Und hier ist eine in 49 Sekunden. Enums sind großartig (und ich liebe sie und benutze sie sehr oft), aber welche Art von Sicherheit benötigen Sie, wenn Sie eine Konstante deklarieren oder verwenden. Es ist ein Overkill, jedes Mal eine Aufzählung zu erstellen, wenn Sie eine Konstante für das String-Literal deklarieren müssen.
Shahzeb

4
@Shahzeb Wenn Sie eine einzelne Variable haben, verwenden Sie sicher eine Zeichenfolge. Hier kann nicht viel passieren (ein einzelner Wert ist als Parameter eher sinnlos). Aber beachten Sie, dass wir reden über konstant S, so dass jetzt reden wir über sie wahrscheinlich in Funktionen vorbei, usw. Haben wir brauchen -saftey geben? Nun, nein, aber dann betrachten die meisten Leute C-Style-Typen von "Alles ist ein void*" guter Stil und es kann Fehler stoppen (insbesondere wenn mehr als ein Enum / String-Parameter übergeben wird!). Außerdem werden die Konstanten in ihren eigenen Namespace usw. eingefügt. Im Gegensatz dazu gibt es keinen wirklichen Vorteil, nur einfache Variablen zu haben.
Voo

3
@Bohemian: Ich verstehe nicht wie. Mit ints gibt es keine Typensicherheit, da man jeden Wert übergeben kann. Typisierte Objekte unterscheiden sich dagegen in Bezug auf die Typensicherheit nicht von Aufzählungen.
Casablanca

Antworten:


78

Technisch könnte man Enums tatsächlich als eine Klasse mit einer Reihe typisierter Konstanten betrachten, und auf diese Weise werden Enum-Konstanten tatsächlich intern implementiert. Die Verwendung von enumbietet Ihnen jedoch nützliche Methoden ( Enum javadoc ), die Sie sonst selbst implementieren müssten, z Enum.valueOf.


14
Es besteht auch .values()die Möglichkeit, die Werteliste zu durchlaufen.
h3xStream

1
Dies scheint die richtige Antwort zu sein, obwohl sie nicht sehr befriedigend ist. Meiner Meinung nach hat es sich für Java kaum gelohnt, Enums nur für die kompaktere Syntax und den Zugriff auf die Enum-Methoden zu unterstützen.
Craig W

7
@Craig Ihre Instinkte sind richtig - dies ist eine sehr schlechte Antwort, weil es den Zweck von Aufzählungen völlig verfehlt hat. Siehe meine Kommentare unter der Frage für einen Teil der Gründe warum.
Böhmisch

1
@Bohemian: Ich habe den Zweck von Aufzählungen nicht "verpasst" - ich benutze sie die ganze Zeit. Siehe meine Antwort auf Ihren Kommentar oben.
Casablanca

1
Die Enum.valuesOf-Methode gibt dasselbe Objekt zurück, wenn sie zweimal aufgerufen wird.
Emre Aktürk

104
  1. Typensicherheit und Wertesicherheit.
  2. Garantierter Singleton.
  3. Fähigkeit, Methoden zu definieren und zu überschreiben.
  4. Fähigkeit, Werte in switchAnweisungen zu verwendencase Aussagen ohne Qualifikation.
  5. Eingebaute Sequenzierung von Werten über ordinal().
  6. Serialisierung nach Namen und nicht nach Wert, was ein gewisses Maß an Zukunftssicherheit bietet.
  7. EnumSetund EnumMapKlassen.

19
Trotzdem habe ich es jedes Mal bereut, wenn ich Code in eine Aufzählung eingefügt habe.
Marquis von Lorne

4
Warum hast du es bereut? Ich habe nie ...
glglgl

2
@glglgl Weil es anwendungsspezifischen Code an einen Ort gebracht hat, an dem ich das Gefühl hatte, dass er nicht wirklich dazu gehört, sondern nur eine Reihe von Werten definiert. Wenn ich es noch einmal tun müsste, hätte ich es in eine der zahlreichen switchAussagen aufgenommen, die die ursprüngliche Motivation für die Verwendung eines Enumüberhaupt waren.
Marquis von Lorne

72

Niemand erwähnte die Fähigkeit, sie in switchAussagen zu verwenden; Ich werde das auch einwerfen.

Auf diese Weise können beliebig komplexe Aufzählungen auf saubere Weise verwendet werden, ohne dass instanceofmöglicherweise verwirrende ifSequenzen oder Nicht-String / Int-Schaltwerte verwendet werden. Das kanonische Beispiel ist eine Zustandsmaschine.


Wie auch immer, Sie haben keine Vorteile von Aufzählungen gegenüber statischen Feldern erwähnt. Sie können genügend Typen in switch-Anweisungen mit statischen Feldern verwenden. Op Brauchen Sie echte Funktionen oder Leistungsunterschiede
Genaut

@Genaut Der Vorteil ist, dass Aufzählungen mehr Funktionen haben als eine Zeichenfolge oder int - die Frage war nach Unterschieden, die ich bereitgestellt habe. Das OP weiß bereits, was Aufzählungen sind, und niemand anderes hatte switch-Anweisungen erwähnt, als ich diese vor 4,5 Jahren veröffentlichte, und zumindest einige Leute fanden, dass sie neue Informationen lieferten ¯_ (ツ) _ / ¯
Dave Newton

44

Der Hauptvorteil ist die Typensicherheit. Mit einer Reihe von Konstanten kann jeder Wert desselben intrinsischen Typs verwendet werden, was zu Fehlern führt. Mit einer Aufzählung können nur die zutreffenden Werte verwendet werden.

Beispielsweise

public static final int SIZE_SMALL  = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE  = 3;

public void setSize(int newSize) { ... }

obj.setSize(15); // Compiles but likely to fail later

vs.

public enum Size { SMALL, MEDIUM, LARGE };

public void setSize(Size s) { ... }

obj.setSize( ? ); // Can't even express the above example with an enum

3
Klassen sind auch typsicher ...: / (vorausgesetzt, statische Felder sind vom Typ der Containerklasse)
h3xStream

Sie könnten immer noch einen ungültigen Wert übergeben, aber es wäre ein Fehler bei der Kompilierung, der viel einfacher zu erkennen ist.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

2
Sie könnten setSize(null)Ihr zweites Beispiel aufrufen , aber es wird wahrscheinlich viel früher als der Fehler des ersten Beispiels fehlschlagen.
Jeffrey

42

Es gibt weniger Verwirrung. Nehmen Sie Fontzum Beispiel. Es hat einen Konstruktor, der den Namen des Fontgewünschten Konstruktors , seine Größe und seinen Stil ( new Font(String, int, int)) übernimmt . Bis heute kann ich mich nicht erinnern, ob Stil oder Größe an erster Stelle stehen. Wenn Fontein benutzt hat , enumfür alle seine verschiedenen Stile ( PLAIN, BOLD, ITALIC, BOLD_ITALIC), würde der Konstruktor aussehen Font(String, Style, int), jede Verwirrung zu vermeiden. Leider gab enumes bei der FontErstellung der Klasse keine s , und da Java die umgekehrte Kompatibilität beibehalten muss, werden wir immer von dieser Mehrdeutigkeit geplagt sein.

Dies ist natürlich nur ein Argument für die Verwendung von enumanstelle von public static finalKonstanten. Aufzählungen eignen sich auch perfekt für Singletons und die Implementierung von Standardverhalten, während eine spätere Anpassung möglich ist (dh das Strategiemuster ). Ein Beispiel für letzteres ist java.nio.file‚s OpenOptionund StandardOpenOption: wenn ein Entwickler seinen eigenen Nicht-Standard schaffen wollte OpenOption, er konnte.


Ihr 'Font'-Fall ist immer noch nicht eindeutig, wenn zwei gleiche Aufzählungen angefordert wurden. Die kanonische Antwort auf dieses Problem ist das, was viele Sprachen als benannte Parameter bezeichnen . Diese oder eine bessere Unterstützung der IDE-Methodensignatur.
aaaaaa

1
@aaaaaa Ich habe nicht viele Fälle gesehen, in denen ein Konstruktor zwei davon nehmen würde, enumohne varargs oder a Setzu verwenden, um eine beliebige Anzahl von ihnen zu nehmen.
Jeffrey

@aaaaaa Das Hauptproblem bei benannten Parametern beruht auf Implementierungsdetails (dem Namen des Parameters). Ich kann eine Schnittstelle machen interface Foo { public void bar(String baz); }. Jemand macht eine Klasse, die aufruft bar: someFoo.bar(baz = "hello");. Ich ändere die Unterschrift von Foo::barauf public void bar(String foobar). Jetzt muss die Person, die angerufen someFoohat, ihren Code ändern, wenn sie weiterhin funktionieren soll.
Jeffrey

Ich erinnere mich auch nicht daran, aufeinanderfolgende Aufzählungstypen gesehen zu haben, dachte aber, ein häufiger Typ könnte DAY_OF_WEEK oder ähnliches sein. Und ein guter Punkt über die Schnittstelle - daran hatte ich nicht gedacht. Persönlich würde ich dieses Problem über die weit verbreitete Mehrdeutigkeit hinwegführen, die durch nicht benannte Parameter verursacht wird und eine stärkere IDE-Unterstützung erfordert. Ich verstehe jedoch, dass dies ein Urteilsspruch ist und dass das Brechen von API-Änderungen ernsthaft in Betracht gezogen werden muss.
aaaaaa

26

Hier gibt es viele gute Antworten, aber keine erwähnt, dass es hochoptimierte Implementierungen der Collection API-Klassen / -Schnittstellen speziell für Enums gibt :

Diese enumspezifischen Klassen akzeptieren nur EnumInstanzen (die EnumMapeinzigen akzeptieren nur Enums als Schlüssel) und kehren, wann immer möglich, in ihrer Implementierung zu einer kompakten Darstellung und Bitmanipulation zurück.

Was bedeutet das?

Wenn unser EnumTyp nicht mehr als 64 Elemente enthält (die meisten Beispiele aus dem wirklichen Leben sind Enumdafür geeignet), speichern die Implementierungen die Elemente in einem einzigen longWert. Jede Enumbetreffende Instanz wird mit einem Bit dieser 64-Bit-Länge verknüpft long. Das Hinzufügen eines Elements zu einem EnumSetsetzt einfach das richtige Bit auf 1, das Entfernen setzt nur das Bit auf 0. Das Testen, ob sich ein Element im Element befindet, Setist nur ein Bitmaskentest! Jetzt musst du Enums dafür lieben !


1
Ich wusste schon früher über diese beiden Bescheid, aber ich habe gerade viel von Ihrer What does this mean?Abteilung gelernt . Ich wusste, dass es bei Größe 64 eine Kluft in der Implementierung gab, aber ich wusste nicht wirklich warum
Christopher Rucinski

15

Beispiel:

public class CurrencyDenom {
   public static final int PENNY = 1;
 public static final int NICKLE = 5;
 public static final int DIME = 10;
public static final int QUARTER = 25;}

Einschränkung von Java-Konstanten

1) Keine Typensicherheit : Erstens ist es nicht typsicher; Sie können int einen gültigen int-Wert zuweisen, z. B. 99, obwohl es keine Münze gibt, die diesen Wert darstellt.

2) Kein aussagekräftiger Druck : Der Druckwert einer dieser Konstanten gibt den numerischen Wert anstelle des aussagekräftigen Münznamens aus. Wenn Sie beispielsweise NICKLE drucken, wird "5" anstelle von "NICKLE" gedruckt.

3) Kein Namespace : Um auf die KonstanterencyDenom zuzugreifen, müssen wir dem Klassennamen, z. B. CurrencyDenom.PENNY, ein Präfix voranstellen, anstatt nur PENNY zu verwenden. Dies kann jedoch auch durch statischen Import in JDK 1.5 erreicht werden

Vorteil der Aufzählung

1) Aufzählungen in Java sind typsicher und haben einen eigenen Namensraum. Dies bedeutet, dass Ihre Aufzählung im folgenden Beispiel den Typ "Währung" hat und Sie keinen anderen Wert als den in Aufzählungskonstanten angegebenen zuweisen können.

public enum Currency {PENNY, NICKLE, DIME, QUARTER};

Currency coin = Currency.PENNY; coin = 1; //compilation error

2) Enum in Java sind Referenztypen wie Klasse oder Schnittstelle. Sie können Konstruktoren, Methoden und Variablen in Java Enum definieren, wodurch es leistungsfähiger als Enum in C und C ++ ist, wie im nächsten Beispiel des Java Enum-Typs gezeigt.

3) Sie können zum Zeitpunkt der Erstellung Werte für Enum-Konstanten angeben, wie im folgenden Beispiel gezeigt: public enum Currency {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Damit dies funktioniert, müssen Sie eine Mitgliedsvariable und einen Konstruktor definieren, da PENNY (1) tatsächlich einen Konstruktor aufruft, der den int-Wert akzeptiert (siehe Beispiel unten).

public enum Currency {
    PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
    private int value;

    private Currency(int value) {
            this.value = value;
    }
}; 

Referenz: https://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html


11

Der erste Vorteil von Aufzählungen ist, wie Sie bereits bemerkt haben, die Einfachheit der Syntax. Der Hauptpunkt von Aufzählungen besteht jedoch darin, einen bekannten Satz von Konstanten bereitzustellen, die standardmäßig einen Bereich bilden und dazu beitragen, eine umfassendere Codeanalyse durch Sicherheitsüberprüfungen für Typ und Wert durchzuführen.

Diese Attribute von Enums helfen sowohl einem Programmierer als auch einem Compiler. Angenommen, Sie sehen eine Funktion, die eine Ganzzahl akzeptiert. Was könnte diese ganze Zahl bedeuten? Welche Werte können Sie weitergeben? Sie wissen es nicht sofort. Wenn Sie jedoch eine Funktion sehen, die Enum akzeptiert, kennen Sie alle möglichen Werte, die Sie übergeben können, sehr gut.

Für den Compiler helfen Aufzählungen dabei, einen Wertebereich zu bestimmen. Wenn Sie Aufzählungsmitgliedern keine speziellen Werte zuweisen, handelt es sich um Bereiche von 0 und höher. Dies hilft, Fehler im Code durch Typensicherheitsprüfungen und mehr automatisch aufzuspüren. Beispielsweise kann der Compiler Sie warnen, dass Sie nicht alle möglichen Aufzählungswerte in Ihrer switch-Anweisung behandeln (dh wenn Sie keine defaultGroß- und Kleinschreibung haben und nur einen von N Aufzählungswerten behandeln). Es warnt Sie auch, wenn Sie eine beliebige Ganzzahl in eine Ganzzahl konvertieren, da der Wertebereich von enum kleiner als der von Ganzzahlen ist und dies wiederum Fehler in der Funktion auslösen kann, die eine Ganzzahl nicht wirklich akzeptiert. Außerdem wird das Generieren einer Sprungtabelle für den Schalter einfacher, wenn die Werte von 0 und höher sind.

Dies gilt nicht nur für Java, sondern auch für andere Sprachen mit einer strengen Typprüfung. C, C ++, D, C # sind gute Beispiele.


4

Eine Aufzählung ist implizit endgültig. Bei privaten Konstruktoren sind alle ihre Werte vom gleichen Typ oder Untertyp. Sie können alle ihre Werte mit values()abrufen, ihren name()oder ordinal()Wert abrufen oder eine Aufzählung nach Nummer oder Name suchen.

Sie können auch Unterklassen definieren (obwohl dies fiktiv endgültig ist, was Sie auf keine andere Weise tun können)

enum Runner implements Runnable {
    HI {
       public void run() {
           System.out.println("Hello");
       }
    }, BYE {
       public void run() {
           System.out.println("Sayonara");
       }
       public String toString() {
           return "good-bye";
       }
    }
 }

 class MYRunner extends Runner // won't compile.

4

Aufzählung Vorteile:

  1. Aufzählungen sind typsicher, statische Felder nicht
  2. Es gibt eine endliche Anzahl von Werten (es ist nicht möglich, nicht vorhandene Aufzählungswerte zu übergeben. Wenn Sie statische Klassenfelder haben, können Sie diesen Fehler machen).
  3. Jeder Aufzählung können mehrere Eigenschaften (Felder / Getter) zugewiesen werden - Kapselung. Auch einige einfache Methoden: YEAR.toSeconds () oder ähnliches. Vergleichen Sie: Colors.RED.getHex () mit Colors.toHex (Colors.RED)

"wie die Fähigkeit, einem Aufzählungselement leicht einen bestimmten Wert zuzuweisen"

enum EnumX{
  VAL_1(1),
  VAL_200(200);
  public final int certainValue;
  private X(int certainValue){this.certainValue = certainValue;}
}

"und folglich die Fähigkeit, eine Ganzzahl ohne großen Aufwand in eine Aufzählung umzuwandeln" Fügen Sie eine Methode hinzu, die int in eine Aufzählung umwandelt, die dies tut. Fügen Sie einfach die statische HashMap <Integer, EnumX> hinzu, die die Mapping- Java- Enumeration enthält .

Wenn Sie ord = VAL_200.ordinal () wirklich zurück in val_200 konvertieren möchten, verwenden Sie einfach: EnumX.values ​​() [ord]


3

Ein weiterer wichtiger Unterschied besteht darin, dass der Java-Compiler static finalFelder primitiver Typen und Zeichenfolgen als Literale behandelt. Dies bedeutet, dass diese Konstanten inline werden. Es ist ähnlich wie C/C++ #definePräprozessor. Siehe diese SO-Frage . Dies ist bei Aufzählungen nicht der Fall.



2

Der größte Vorteil ist enum Singletons sind einfach zu schreiben und threadsicher:

public enum EasySingleton{
    INSTANCE;
}

und

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

beide sind ähnlich und es handhabte die Serialisierung selbst durch Implementierung

//readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }

Mehr


0

Ich denke ein enumkann nicht sein final, weil unter der Haube Compiler Unterklassen für jede generiertenum Eintrag .

Weitere Informationen Aus der Quelle


Intern sind sie nicht endgültig, da sie - wie Sie sagen - intern in Unterklassen unterteilt werden können. Leider können Sie sie nicht selbst unterordnen, z. B. um sie um eigene Werte zu erweitern.
glglgl

0

Es gibt viele Vorteile von Aufzählungen, die hier veröffentlicht werden, und ich erstelle gerade solche Aufzählungen, wie in der Frage gestellt. Aber ich habe eine Aufzählung mit 5-6 Feldern.

enum Planet{
EARTH(1000000, 312312321,31232131, "some text", "", 12),
....
other planets
....

In solchen Fällen ist es sehr schwierig zu verstehen, welcher Wert zu welchem ​​Feld gehört, wenn Sie Konstruktor und Augapfel sehen müssen, wenn Sie mehrere Felder in Aufzählungen haben.

Eine Klasse mit static finalKonstanten und die Verwendung von BuilderMustern zum Erstellen solcher Objekte macht sie lesbarer. Sie würden jedoch alle anderen Vorteile der Verwendung einer Aufzählung verlieren, wenn Sie sie benötigen. Ein Nachteil solcher Klassen ist, dass Sie die PlanetObjekte manuell zum list/setvon hinzufügen müssenPlanets.

Ich bevorzuge immer noch Enum gegenüber solchen Klassen, da dies values()praktisch ist und man nie weiß, ob man sie in switchoder EnumSetoder EnumMapin Zukunft verwenden muss :)


0

Hauptgrund: Enums helfen Ihnen dabei, gut strukturierten Code zu schreiben, bei dem die semantische Bedeutung von Parametern beim Kompilieren klar und stark typisiert ist - aus all den Gründen, die andere Antworten angegeben haben.

Quid pro quo: In Java ist die Anzahl der Mitglieder einer Enum endgültig. Das ist normalerweise gut, da es die Sicherheit und das Testen verbessert, aber in einigen Situationen kann es ein Nachteil sein, beispielsweise wenn Sie vorhandenen Basiscode möglicherweise aus einer Bibliothek erweitern. Wenn sich dieselben Daten in einer Klasse mit statischen Feldern befinden, können Sie zur Laufzeit problemlos neue Instanzen dieser Klasse hinzufügen (möglicherweise müssen Sie auch Code schreiben, um diese zu einer Iterable hinzuzufügen, die Sie für diese Klasse haben). Dieses Verhalten von Enums kann jedoch geändert werden: Mithilfe von Reflection können Sie zur Laufzeit neue Mitglieder hinzufügen oder vorhandene Mitglieder ersetzen. Dies sollte jedoch wahrscheinlich nur in speziellen Situationen erfolgen, in denen es keine Alternative gibt: dh es handelt sich um eine hackige Lösung, die zu unerwarteten Problemen führen kann. siehe meine Antwort aufKann ich hinzufügen und Elemente der Aufzählung zur Laufzeit in Java entfernen .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.