Perché utilizzare una funzione del linguaggio di programmazione? Il motivo per cui abbiamo le lingue è per
- I programmatori possono esprimere in modo efficiente e corretto algoritmi in una forma che i computer possono usare.
- I manutentori per capire gli algoritmi che altri hanno scritto e apportare correttamente le modifiche.
Gli enum migliorano sia la probabilità di correttezza che la leggibilità senza scrivere molta piastra di caldaia. Se sei disposto a scrivere boilerplate, allora puoi "simulare" enum:
public class Color {
private Color() {} // Prevent others from making colors.
public static final Color RED = new Color();
public static final Color AMBER = new Color();
public static final Color GREEN = new Color();
}
Ora puoi scrivere:
Color trafficLightColor = Color.RED;
La targhetta sopra ha lo stesso effetto di
public enum Color { RED, AMBER, GREEN };
Entrambi forniscono lo stesso livello di controllo della guida dal compilatore. Boilerplate è solo più digitando. Ma salvare un sacco di digitazione rende il programmatore più efficiente (vedi 1), quindi è una funzione utile.
Vale la pena anche per almeno un'altra ragione:
Istruzioni switch
Una cosa che la static final
simulazione enum di cui sopra non ti dà sono i bei switch
casi. Per i tipi di enum, lo switch Java utilizza il tipo della sua variabile per inferire l'ambito dei casi enum, quindi per quanto enum Color
sopra devi semplicemente dire:
Color color = ... ;
switch (color) {
case RED:
...
break;
}
Nota che non è Color.RED
nei casi. Se non usi enum, l'unico modo per usare le quantità con nome switch
è qualcosa di simile a:
public Class Color {
public static final int RED = 0;
public static final int AMBER = 1;
public static final int GREEN = 2;
}
Ma ora una variabile per contenere un colore deve avere tipo int
. Il piacevole compilatore che controlla l'enum e ilstatic final
simulazione è sparito. Non felice.
Un compromesso consiste nell'utilizzare un membro con valore scalare nella simulazione:
public class Color {
public static final int RED_TAG = 1;
public static final int AMBER_TAG = 2;
public static final int GREEN_TAG = 3;
public final int tag;
private Color(int tag) { this.tag = tag; }
public static final Color RED = new Color(RED_TAG);
public static final Color AMBER = new Color(AMBER_TAG);
public static final Color GREEN = new Color(GREEN_TAG);
}
Adesso:
Color color = ... ;
switch (color.tag) {
case Color.RED_TAG:
...
break;
}
Ma nota, ancora più boilerplate!
Usando un enum come singleton
Dalla piastra sopra riportata puoi vedere perché un enum fornisce un modo per implementare un singleton. Invece di scrivere:
public class SingletonClass {
public static final void INSTANCE = new SingletonClass();
private SingletonClass() {}
// all the methods and instance data for the class here
}
e poi accedendo con
SingletonClass.INSTANCE
possiamo solo dire
public enum SingletonClass {
INSTANCE;
// all the methods and instance data for the class here
}
che ci dà la stessa cosa. Possiamo evitarlo perché gli enum Java sono implementati come classi complete con solo un po 'di zucchero sintattico cosparso sopra. Questo è di nuovo meno targhetta, ma non è ovvio a meno che il linguaggio non ti sia familiare. Non mi piace anche il fatto che ottieni le varie funzioni enum anche se non hanno molto senso per il singleton: ord
e values
, ecc. (In realtà c'è una simulazione più complicata in cui Color extends Integer
funzionerà con switch, ma è così complicato che è ancora più mostra chiaramente perché enum
è un'idea migliore.)
Sicurezza del filo
La sicurezza del thread è un potenziale problema solo quando i singoli vengono creati pigramente senza blocco.
public class SingletonClass {
private static SingletonClass INSTANCE;
private SingletonClass() {}
public SingletonClass getInstance() {
if (INSTANCE == null) INSTANCE = new SingletonClass();
return INSTANCE;
}
// all the methods and instance data for the class here
}
Se molti thread chiamano getInstance
contemporaneamente mentre INSTANCE
è ancora nullo, è possibile creare un numero qualsiasi di istanze. Questo non va bene. L'unica soluzione è aggiungere l' synchronized
accesso per proteggere la variabile INSTANCE
.
Tuttavia, il static final
codice sopra non presenta questo problema. Crea l'istanza avidamente al momento del caricamento della classe. Il caricamento della classe è sincronizzato.
Il enum
singleton è effettivamente pigro perché non è inizializzato fino al primo utilizzo. Anche l'inizializzazione Java è sincronizzata, quindi più thread non possono inizializzare più di un'istanza di INSTANCE
. Stai ricevendo un singleton pigramente inizializzato con pochissimo codice. L'unico aspetto negativo è la sintassi piuttosto oscura. Devi conoscere il linguaggio o comprendere a fondo come funzionano il caricamento e l'inizializzazione della classe per sapere cosa sta succedendo.