Qual è il vantaggio di un enum di Java rispetto a una class con campi finali statici pubblici?

Ho molta familiarità con C # ma sto iniziando a lavorare di più in Java. Mi aspettavo di sapere che le enumerazioni in Java erano sostanzialmente equivalenti a quelle in C # ma a quanto pare non è questo il caso. Inizialmente ero entusiasta di apprendere che le enumerazioni Java potevano contenere più parti di dati che sembrano molto vantaggiose ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Tuttavia, da allora ho trovato molte caratteristiche mancanti che sono banali in C #, come la possibilità di assegnare facilmente un elemento enum a un determinato valore e, di conseguenza, la possibilità di convertire un intero in enum senza una quantità decente di sforzo ( vale a dire convertire il valore intero per abbinare Java Enum ).

Quindi la mia domanda è questa: c’è qualche vantaggio per le enumerazioni Java su una class con una serie di campi finali statici pubblici? O fornisce solo una syntax più compatta?

EDIT: vorrei essere più chiaro. Qual è il vantaggio delle enumerazioni Java su una class con una serie di campi finali statici pubblici dello stesso tipo ? Ad esempio, nell’esempio dei pianeti al primo collegamento, qual è il vantaggio di un enum su una class con queste costanti pubbliche:

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

Per quanto posso dire, la risposta di Casablanca è l’unica che soddisfa questo.

Dal punto di vista tecnico, si potrebbe effettivamente considerare l’enumerazione come una class con una serie di costanti tipizzate, e questo è in effetti il ​​modo in cui le costanti enum vengono implementate internamente. L’uso di un enum fornisce comunque metodi utili ( Enum javadoc ) che altrimenti dovresti implementare tu stesso, come Enum.valueOf .

  1. Digitare sicurezza e sicurezza del valore.
  2. Singleton garantito.
  3. Capacità di definire e sovrascrivere i metodi.
  4. Possibilità di utilizzare i valori nelle dichiarazioni dei case switch statement senza qualifica.
  5. Sequentializzazione dei valori incorporata tramite ordinal().
  6. Serializzazione per nome e non per valore, che offre un grado di impermeabilità futura.
  7. EnumSet ed EnumMap .

Nessuno ha menzionato la possibilità di usarli nelle dichiarazioni switch ; Lo butterò anche io.

Ciò consente di utilizzare enumerazioni arbitrariamente complesse in modo pulito senza utilizzare instanceof , potenzialmente confondendo sequenze o valori di commutazione non stringa / int. L’esempio canonico è una macchina a stati.

C’è meno confusione. Prendi Font per esempio. Ha un costruttore che prende il nome del Font che vuoi, la sua dimensione e il suo stile ( new Font(String, int, int) ). Fino ad oggi non riesco a ricordare se lo stile o le dimensioni sono le prime. Se Font avesse usato un enum per tutti i suoi diversi stili ( PLAIN , BOLD , ITALIC , BOLD_ITALIC ), il suo costruttore sarebbe simile a Font(String, Style, int) , evitando qualsiasi confusione. Sfortunatamente, enum s non esisteva quando la class Font è stata creata, e dal momento che Java deve mantenere la compatibilità inversa, saremo sempre afflitti da questa ambiguità.

Naturalmente, questo è solo un argomento per usare un enum posto delle costanti public static final . Le enumerazioni sono perfette anche per i singleton e implementano il comportamento predefinito, consentendo al tempo stesso la personalizzazione (IE, il modello strategico ). Un esempio di quest’ultimo è OpenOption e StandardOpenOption java.nio.file : se uno sviluppatore voleva creare il proprio OpenOption non standard, poteva farlo.

Il vantaggio principale è la sicurezza del tipo. Con un insieme di costanti, è ansible utilizzare qualsiasi valore dello stesso tipo intrinseco, introducendo errori. Con un enum possono essere usati solo i valori applicabili.

Per esempio

 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 

Ci sono molte buone risposte qui, ma nessuna menziona il fatto che esistono implementazioni altamente ottimizzate delle classi / interfacce API di raccolta specifiche per l’enumerazione :

  • EnumSet
  • EnumMap

Queste classi enum specifiche accettano solo istanze Enum (l’ EnumMap accetta solo Enum solo come chiavi) e, quando ansible, ritornano alla rappresentazione compatta e alla manipolazione dei bit nella loro implementazione.

Cosa significa questo?

Se il nostro tipo Enum non ha più 64 elementi (la maggior parte degli esempi Enum vita reale si qualificheranno per questo), le implementazioni memorizzano gli elementi in un singolo valore long , ogni istanza Enum in questione sarà associata a un bit di questo 64- un po ‘lungo Aggiungere un elemento a un EnumSet è semplicemente impostare il bit corretto su 1, rimuoverlo è solo impostare quel bit su 0. Il test se un elemento è nel Set è solo un test di maschera di bit! Ora devi amare Enum s per questo!

Il primo beneficio dell’enumerazione, come hai già notato, è la semplicità della syntax. Ma il punto principale delle enumerazioni consiste nel fornire un insieme ben noto di costanti che, per impostazione predefinita, formano un intervallo e aiutano a eseguire un’analisi di codice più completa attraverso controlli di sicurezza di tipo e valore.

Questi attributi di enumerazione aiutano sia un programmatore che un compilatore. Ad esempio, supponiamo che tu veda una funzione che accetta un intero. Cosa potrebbe significare questo intero? Che tipo di valori puoi trasmettere? Non lo sai davvero subito. Ma se vedi una funzione che accetta enum, conosci molto bene tutti i possibili valori che puoi trasmettere.

Per il compilatore, le enumerazioni aiutano a determinare un intervallo di valori e, a meno che non si assegnino valori speciali ai membri di enum, essi vanno bene da 0 in su. Questo aiuta a rintracciare automaticamente gli errori nel codice attraverso controlli di sicurezza del tipo e altro. Ad esempio, il compilatore potrebbe avvisarti che non gestisci tutti i possibili valori enum nella tua istruzione switch (cioè quando non hai un caso default e gestisci solo uno dei valori N enum). Ti avvisa anche quando converti un intero arbitrario in enum perché l’intervallo di valori di enum è inferiore a quello intero e che a sua volta può innescare errori nella funzione che non accetta realmente un numero intero. Inoltre, generare una tabella di salto per l’interruttore diventa più semplice quando i valori vanno da 0 in su.

Questo non è vero solo per Java, ma anche per altre lingue con un rigoroso controllo dei tipi. C, C ++, D, C # sono buoni esempi.

esempio:

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

Limitazione delle costanti java

1) Nessuna sicurezza del tipo : prima di tutto non è sicuro dal punto di vista del tipo; puoi assegnare qualsiasi valore int valido a int es. 99 sebbene non ci sia una moneta per rappresentare quel valore.

2) Nessuna stampa significativa : il valore di stampa di ognuna di queste costanti stamperà il suo valore numerico anziché il nome significativo della moneta, ad esempio quando stampi NICKLE stamperà “5” invece di “NICKLE”

3) Nessuno spazio dei nomi : per accedere alla costante Valuta costante abbiamo bisogno di prefisso il nome della class, ad esempio CurrencyDenom.PENNY invece di usare solo PENNY, anche se questo può essere ottenuto anche utilizzando l’importazione statica in JDK 1.5

Vantaggio di enum

1) Gli enum in Java sono sicuri per tipo e hanno un proprio spazio dei nomi. Significa che l’enumerazione avrà un tipo ad esempio “Valuta” nell’esempio sottostante e non è ansible assegnare alcun valore diverso da quello specificato in Costanti Enum.

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

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

2) Enum in Java è un tipo di riferimento come class o interfaccia ed è ansible definire costruttore, metodi e variabili all’interno di java Enum che lo rende più potente di Enum in C e C ++ come mostrato nel prossimo esempio di tipo Java Enum.

3) È ansible specificare i valori delle costanti enum al momento della creazione come mostrato nell’esempio seguente: public enum Currency {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Ma affinché funzioni, è necessario definire una variabile membro e un costruttore perché PENNY (1) sta effettivamente chiamando un costruttore che accetta il valore int, vedi sotto l’esempio.

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

};

refrence: http://javarevisited.blogspot.in/2011/08/enum-in-java-example-tutorial.html

enum Vantaggi:

  1. Le enumerazioni sono sicure per tipo, i campi statici no
  2. Esiste un numero finito di valori (non è ansible passare il valore dell’enumerazione non esistente. Se si hanno campi di class statici, è ansible commettere l’errore)
  3. Ogni enum può avere più proprietà (campi / getter) assegnati – incapsulamento. Anche alcuni semplici metodi: YEAR.toSeconds () o simili. Confronta: Colors.RED.getHex () con Colors.toHex (Colors.RED)

“come la possibilità di assegnare facilmente un elemento enum a un determinato valore”

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

“e di conseguenza la capacità di convertire un intero in enum senza una quantità decente di sforzi” Aggiungi un metodo per convertire int in enum che lo fa. Basta aggiungere HashMap statico contenente la mapping.

Se vuoi veramente convertire ord = VAL_200.ordinal () di nuovo in val_200 usa solo: EnumX.values ​​() [ord]

Un enum è implicitamente definitivo, con un costruttore privato, tutti i suoi valori sono dello stesso tipo o di un sottotipo, è ansible ottenere tutti i suoi valori usando values() , ottiene il suo name() o ordinal() o si può guardare su un enum per numero o nome.

Puoi anche definire sottoclassi (anche se non necessariamente definitive, qualcosa che non puoi fare in altro modo)

 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. 

Un’altra importante differenza è che il compilatore java considera static final campi static final di tipi primitivi e String come valori letterali. Significa che queste costanti diventano in linea. È simile al preprocessore #define C/C++ . vedi questa domanda SO. Questo non è il caso delle enumerazioni.

Si ottiene il controllo del tempo di compilazione dei valori validi quando si utilizza un enum. Guarda questa domanda

Il più grande vantaggio è enum Singletons sono facili da scrivere e thread-safe:

 public enum EasySingleton{ INSTANCE; } 

e

 /** * 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; } } 

entrambi sono simili e ha gestito la serializzazione autonomamente implementando

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

Di Più

Penso che un enum non può essere final , perché sotto il cofano compilatore genera sottoclassi per ogni voce enum .

Altre informazioni Dalla fonte

È generalmente considerato una ctriggers pratica. Il problema è che le costanti fanno parte della “interfaccia” pubblica (per mancanza di una parola migliore) della class di implementazione. Ciò significa che la class di implementazione pubblica tutti questi valori su classi esterne anche quando sono richieste solo internamente. Le costanti proliferano attraverso il codice. Un esempio è l’interfaccia SwingConstants in Swing, che è implementata da dozzine di classi che tutti “riesportano” tutte le sue costanti (anche quelle che non usano) come proprie.
Il modello di interfaccia costante è un cattivo uso delle interfacce. Che una class usi alcune costanti internamente è un dettaglio di implementazione. L’implementazione di un’interfaccia costante fa sì che i dettagli di questa implementazione si diffondano nell’API esportata della class. Non ha alcuna conseguenza per gli utenti di una class che la class implementa un’interfaccia costante. In effetti, potrebbe persino confonderli. Peggio ancora, rappresenta un impegno: se in una versione futura la class viene modificata in modo tale da non dover più utilizzare le costanti, è comunque necessario implementare l’interfaccia per garantire la compatibilità binaria. Se una class non definitiva implementa un’interfaccia costante, tutte le sue sottoclassi avranno i loro spazi dei nomi contaminati dalle costanti nell’interfaccia.
Un enum potrebbe essere un approccio migliore. O potresti semplicemente inserire le costanti come campi statici pubblici in una class che non può essere istanziata. Ciò consente a un’altra class di accedervi senza inquinare la propria API.