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


147

Conosco molto bene C # ma sto iniziando a lavorare di più in Java. Mi aspettavo di apprendere che gli enum in Java erano sostanzialmente equivalenti a quelli in C # ma a quanto pare non è così. Inizialmente ero entusiasta di apprendere che gli enum Java potevano contenere più dati che sembrano molto vantaggiosi ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Tuttavia, da allora ho scoperto che mancano molte funzionalità che sono insignificanti in C #, come la capacità di assegnare facilmente un elemento enum un certo valore e, di conseguenza, la capacità di convertire un numero intero in un enum senza un discreto sforzo ( cioè Converti il ​​valore intero in Java Enum corrispondente ).

Quindi la mia domanda è questa: c'è qualche vantaggio in enum Java rispetto a una classe con un sacco di campi finali statici pubblici? O fornisce semplicemente una sintassi più compatta?

EDIT: Vorrei essere più chiaro. Qual è il vantaggio di Java enum su una classe con un gruppo di campi finali statici pubblici dello stesso tipo ? Ad esempio, nell'esempio dei pianeti al primo collegamento, qual è il vantaggio di un enum rispetto a una classe 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 ne so, la risposta di Casablanca è l'unica che soddisfa questo.


4
@Bohemian: potrebbe non essere un duplicato, poiché l'OP ha menzionato solo i public static finalcampi, che possono essere valori digitati e non necessariamente ints.
Casablanca,

1
@Shahzeb Hardly. Chiaramente usare enum invece di costanti di stringhe è un'ottima idea e molto più che incoraggiato. Sicurezza del tipo, non necessita di funzioni di utilità statiche e così via .. assolutamente nessun motivo per usare invece le stringhe.
Voo

1
@Voo Sì, sapevo che ci sarebbero stati disaccordi. Ed eccone uno in 49 secondi. Gli enum sono fantastici (e li adoro e li uso molto spesso) Ma di quale tipo di sicurezza hai bisogno quando dichiari una costante o la usi. È eccessivo creare enum ogni volta che è necessario dichiarare una costante per String letterale.
Shahzeb

4
@Shahzeb Se hai una singola variabile, usa sicuramente una stringa, qui non può succedere molto (un singolo valore è piuttosto inutile come parametro). Ma nota che stiamo parlando di costante S, quindi ora stiamo parlando probabilmente di passarli a funzioni, ecc. Abbiamo bisogno di sicurezza dei tipi? Bene, no, ma la maggior parte delle persone considera i tipi in stile c di "tutto è un void*" buon stile e può bloccare i bug (specialmente se si passa più di un parametro enum / string!). Inoltre mette le costanti nel loro spazio dei nomi, ecc. Contrariamente a ciò, non c'è alcun vantaggio reale avere solo variabili semplici.
Voo

3
@Bohemian: non vedo come. Con ints, non esiste una sicurezza di tipo perché si potrebbe passare qualsiasi valore. Gli oggetti digitati, d'altra parte, non sono diversi dagli enumerati in termini di sicurezza del tipo.
Casablanca,

Risposte:


78

Tecnicamente si potrebbero effettivamente vedere gli enum come una classe con un gruppo di costanti tipizzate, ed è così che le costanti enum vengono implementate internamente. L'uso di enumcomunque ti dà metodi utili ( Enum javadoc ) che altrimenti dovresti implementare, come Enum.valueOf.


14
È inoltre .values()necessario scorrere l'elenco dei valori.
h3xStream

1
Questa sembra essere la risposta giusta, anche se non è molto soddisfacente. A mio avviso, a malapena valeva la pena per Java aggiungere il supporto per enum solo per la sintassi più compatta e l'accesso ai metodi Enum.
Craig W

7
@Craig il tuo istinto ha ragione: questa è una pessima risposta, perché ha mancato del tutto lo scopo degli enum. Vedi i miei commenti sotto la domanda per una parte dei motivi.
Boemo

1
@Bohemian: non ho "perso lo scopo" degli enum - li uso sempre. Vedi la mia risposta al tuo commento sopra.
Casablanca,

1
Il metodo Enum.valuesOf restituisce lo stesso oggetto se chiamato due volte?
Emre Aktürk,

104
  1. Digitare sicurezza e valore sicurezza.
  2. Singleton garantito.
  3. Capacità di definire e scavalcare i metodi.
  4. Capacità di utilizzare i valori nelle switchdichiarazioni di caseistruzione senza qualifica.
  5. Sequenzializzazione integrata dei valori tramite ordinal().
  6. Serializzazione per nome non per valore, che offre un certo grado di impermeabilità al futuro.
  7. EnumSete EnumMapclassi.

19
Detto questo, ogni volta che ho inserito il codice in un Enum me ne sono pentito.
Marchese di Lorne,

4
Perché te ne sei pentito? Non ho mai ...
glglgl il

2
@glglgl Perché ha messo il codice specifico dell'applicazione in un posto in cui pensavo che non appartenesse davvero, che stava davvero definendo un insieme di valori. Se avessi dovuto farlo di nuovo, l'avrei incluso in una delle numerose switchaffermazioni che erano la motivazione originale per l'utilizzo di un Enumaffatto.
Marchese di Lorne,

72

Nessuno ha menzionato la capacità di usarli nelle switchdichiarazioni; Lo aggiungerò anche io.

Ciò consente di utilizzare enumerazioni arbitrariamente complesse in modo pulito senza utilizzare sequenze instanceofpotenzialmente confuse ifo valori di commutazione non stringa / int. L'esempio canonico è una macchina a stati.


Ad ogni modo, non hai menzionato alcun vantaggio derivante da enum vs campi statici, puoi usare abbastanza tipi nelle istruzioni switch con campi statici. Op Hai bisogno di veri funzionali o differenze di prestazione
Genaut,

@Genaut Il vantaggio è che gli enum hanno più funzionalità di una stringa o int: la domanda riguardava le differenze, che ho fornito. L'OP è già a conoscenza di cosa sono gli enum, e nessun altro aveva menzionato le dichiarazioni di switch quando l'ho pubblicato 4.5 anni fa, e almeno alcune persone hanno trovato che forniva nuove informazioni ¯_ (ツ) _ / ¯
Dave Newton

44

Il vantaggio principale è la sicurezza del tipo. Con un insieme di costanti, è possibile utilizzare qualsiasi valore dello stesso tipo intrinseco, introducendo errori. Con un enum è possibile utilizzare 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

3
le classi sono anche di tipo sicuro ...: / (supponendo che i campi statici siano del tipo della classe contenitore)
h3xStream

Potresti comunque passargli un valore non valido ma sarebbe un errore di compilazione che è molto più facile da individuare.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

2
Potresti chiamare setSize(null)nel tuo secondo esempio, ma è probabile che fallisca molto prima dell'errore del primo esempio.
Jeffrey,

42

C'è meno confusione. Prendi Fontper esempio. Ha un costruttore che prende il nome Fontdesiderato, le sue dimensioni e il suo stile ( new Font(String, int, int)). Ancora oggi non ricordo se lo stile o le dimensioni vanno per primi. Se Fontaveva usato un enumper tutti i suoi diversi stili ( PLAIN, BOLD, ITALIC, BOLD_ITALIC), il suo costruttore sarebbe simile Font(String, Style, int), evitando qualsiasi confusione. Sfortunatamente, enumnon esistevano quando Fontè stata creata la classe e poiché Java deve mantenere la compatibilità inversa, saremo sempre afflitti da questa ambiguità.

Ovviamente, questo è solo un argomento per usare un enuminvece di public static finalcostanti. Gli enum sono anche perfetti per i singoli e per l'implementazione di comportamenti predefiniti, pur consentendo una successiva personalizzazione (ovvero il modello di strategia ). Un esempio di quest'ultimo è java.nio.file's OpenOptione StandardOpenOption: se uno sviluppatore ha voluto creare la propria non-standard OpenOption, che poteva.


Il tuo caso 'Font' è ancora ambiguo se sono stati richiesti due degli stessi enum. La risposta canonica a quel problema è ciò che molte lingue chiamano parametri nominati . Questo o migliore supporto per la firma del metodo IDE.
aaaaaa,

1
@aaaaaa Non ho visto molti casi in cui un costruttore ne prenderebbe due uguali enumsenza usare varargs o a Setper prenderne un numero arbitrario.
Jeffrey,

@aaaaaa Il problema principale con i parametri nominati dipende dai dettagli di implementazione (il nome del parametro). Posso creare un'interfaccia interface Foo { public void bar(String baz); }. Qualcuno fa una classe che invoca bar: someFoo.bar(baz = "hello");. Cambio la firma di Foo::bara public void bar(String foobar). Ora la persona che ha chiamato someFoodovrà modificare il proprio codice se desidera ancora che funzioni.
Jeffrey,

Non ricordo di aver visto neanche i tipi di enum consecutivi, ma ho pensato che uno comune potesse essere DAY_OF_WEEK o qualcosa di simile. E un buon punto sull'interfaccia: non ci avevo pensato. Personalmente prenderei quel problema sulla diffusa ambiguità causata da parametri senza nome, che richiedono un supporto IDE più forte. Capisco tuttavia che è una richiesta di giudizio e che interrompere le modifiche alle API è qualcosa da considerare seriamente.
aaaaaa,

26

Ci sono molte buone risposte qui, ma nessuna menziona che ci sono implementazioni altamente ottimizzate delle classi / interfacce dell'API Collection specificamente per gli enum :

Queste classi specifiche enum accettano solo Enumistanze (l' EnumMapunica accettazioneEnum solo come chiavi) e, quando possibile, ritornano alla rappresentazione compatta e alla manipolazione dei bit nella loro implementazione.

Cosa significa questo?

Se il nostro Enumtipo non ha più di 64 elementi (la maggior parte degli Enumesempi di vita reale si qualificherà per questo), le implementazioni memorizzano gli elementi in un singolo longvalore, ogni Enumistanza in questione sarà associata a un bit di questo 64-bit long. Aggiungere un elemento a un EnumSetsignifica semplicemente impostare il bit corretto su 1, rimuoverlo è semplicemente impostare quel bit su 0. Testare se un elemento è nel Settest è solo un bitmask! Ora devi amare Enums per questo!


1
Conoscevo questi due prima, ma ho appena imparato molto dalla tua What does this mean?sezione. Sapevo che c'era una divisione nell'implementazione alla dimensione 64, ma in realtà non sapevo perché
Christopher Rucinski,

15

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 di Java

1) Nessun tipo di sicurezza : prima di tutto non è sicuro per il tipo; puoi assegnare qualsiasi valore int valido a int, ad es. 99 sebbene non ci siano monete per rappresentare quel valore.

2) Nessuna stampa significativa : il valore di stampa di una di queste costanti stampa il suo valore numerico anziché il nome significativo della moneta, ad es. Quando si stampa NICKLE, verrà stampato "5" anziché "NICKLE"

3) Nessuno spazio dei nomi : per accedere alla costante currencyDenom è necessario aggiungere il prefisso del nome della classe, ad esempio CurrencyDenom.PENNY invece di utilizzare solo PENNY, sebbene ciò possa essere ottenuto anche utilizzando l'importazione statica in JDK 1.5

Vantaggio di enum

1) Gli Enum in Java sono sicuri per i tipi e hanno un proprio spazio dei nomi. Significa che il tuo enum avrà un tipo ad esempio "Valuta" nell'esempio seguente e non puoi 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 classe o interfaccia e puoi 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) È possibile 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) in realtà chiama un costruttore che accetta il valore int, vedere l'esempio seguente.

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

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

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


11

Il primo vantaggio degli enum, come hai già notato, è la semplicità della sintassi. Ma il punto principale di enum è quello di fornire una serie ben nota di costanti che, di default, formano un intervallo e aiutano a eseguire analisi di codice più complete attraverso controlli di sicurezza di tipo e valore.

Questi attributi di enum aiutano sia un programmatore che un compilatore. Ad esempio, supponiamo che tu veda una funzione che accetta un numero intero. Cosa potrebbe significare quel numero 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, gli enum aiutano a determinare un intervallo di valori e, a meno che non si assegnino valori speciali ai membri enum, sono ben intervalli 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 nell'istruzione switch (ovvero quando non hai il defaultcaso e gestisci solo uno dei valori enum N). Ti avverte anche quando converti un numero 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ù facile quando i valori sono da 0 in su.

Questo non vale solo per Java, ma anche per altre lingue con un rigoroso controllo del tipo. C, C ++, D, C # sono buoni esempi.


4

Un enum è implicitamente finale, con un costruttore privato, tutti i suoi valori sono dello stesso tipo o di un sottotipo, puoi ottenere tutti i suoi valori usando values(), ottenere il suo name()o ordinal()valore o puoi cercare un enum per numero o nome.

Puoi anche definire sottoclassi (anche se teoricamente definitive, qualcosa che non puoi fare diversamente)

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

Vantaggi enum:

  1. Gli enumeratori sono sicuri per i tipi, mentre i campi statici no
  2. Esiste un numero finito di valori (non è possibile passare un valore enum inesistente. Se si hanno campi di classe statici, è possibile commettere quell'errore)
  3. A ogni enum possono essere assegnate più proprietà (campi / getter) - incapsulamento. Inoltre alcuni semplici metodi: YEAR.toSeconds () o simili. Confronta: Colors.RED.getHex () con Colors.toHex (Colors.RED)

"come la capacità di assegnare facilmente a un elemento enum un certo 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 numero intero in un enum senza un discreto sforzo" Aggiungi un metodo di conversione da int a enum che lo fa. Basta aggiungere HashMap statico <Intero, EnumX> contenente il mapping java enum .

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


3

Un'altra differenza importante è che il compilatore java tratta i static finalcampi di tipi primitivi e String come valori letterali. Significa che queste costanti diventano in linea. È simile al C/C++ #definepreprocessore. Vedi questa domanda SO . Questo non è il caso degli enum.



2

Il più grande vantaggio è che i singleton enum 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 da soli implementando

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

Di Più


0

Penso che enumnon possa esserlo final, perché sotto il compilatore del cofano genera sottoclassi per ogni enumvoce.

Ulteriori informazioni Dalla fonte


Internamente, non sono definitivi, perché - come dici tu - possono essere sottoclassati internamente. Ma ahimè, non puoi sottoclassarli da solo, ad esempio per estenderlo con i propri valori.
glglgl,

0

Ci sono molti vantaggi di enumerazioni pubblicate qui, e sto creando tali enumerazioni proprio ora, come poste nella domanda. Ma ho un enum con 5-6 campi.

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

In questi tipi di casi, quando si hanno più campi in enum, è molto difficile capire quale valore appartiene a quale campo in quanto è necessario vedere costruttore e bulbo oculare.

La classe con static finalcostanti e l'utilizzo di Builderpattern per creare tali oggetti lo rendono più leggibile. Ma, se ne avessi bisogno, perderesti tutti gli altri vantaggi dell'utilizzo di un enum. Uno svantaggio di tali classi è che è necessario aggiungere Planetmanualmente gli oggetti a list/setdiPlanets.

Preferisco ancora enum rispetto a tale classe, poiché values()è utile e non sai mai se ne hai bisogno per l'uso in switcho EnumSeto EnumMapin futuro :)


0

Motivo principale: Enum ti aiuta a scrivere un codice ben strutturato in cui il significato semantico dei parametri è chiaro e fortemente tipizzato al momento della compilazione, per tutte le ragioni che hanno fornito altre risposte.

Quid pro quo: in Java, la serie di membri di Enum è definitiva. Normalmente è utile in quanto aiuta a valutare la sicurezza e i test, ma in alcune situazioni potrebbe essere uno svantaggio, ad esempio se si sta estendendo il codice di base esistente, forse da una libreria. Al contrario, se gli stessi dati si trovano in una classe con campi statici, è possibile aggiungere facilmente nuove istanze di quella classe in fase di esecuzione (potrebbe anche essere necessario scrivere codice per aggiungerli a qualsiasi Iterable disponibile per quella classe). Ma questo comportamento di Enums può essere modificato: usando reflection puoi aggiungere nuovi membri in fase di esecuzione o sostituire membri esistenti, anche se questo dovrebbe probabilmente essere fatto solo in situazioni specializzate in cui non vi sono alternative: cioè è una soluzione caotica e può produrre problemi imprevisti, vedi la mia risposta suposso aggiungere e rimuovere elementi di enumerazione in fase di esecuzione in Java .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.