Associazione di valori a Java Enum

1. Introduzione

Il tipo enumerazione Java fornisce un modo supportato dal linguaggio per creare e utilizzare valori costanti. Definendo un insieme finito di valori, l' enum è più indipendente dai tipi rispetto alle variabili letterali costanti come String o int .

Tuttavia, i valori enum devono essere identificatori validi e siamo incoraggiati a utilizzare SCREAMING_SNAKE_CASE per convenzione.

Date queste limitazioni, il valore enum da solo non è adatto per stringhe leggibili o valori non stringa .

In questo tutorial, useremo le funzionalità di enum come una classe Java per allegare i valori desiderati.

2. Utilizzo di Java Enum come classe

Spesso creiamo un'enumerazione come un semplice elenco di valori. Ad esempio, ecco le prime due righe della tavola periodica come semplice enumerazione :

public enum Element { H, HE, LI, BE, B, C, N, O, F, NE }

Utilizzando la sintassi precedente, abbiamo creato dieci istanze finali statiche dell'enumerazione denominata Element . Sebbene sia molto efficiente, abbiamo catturato solo i simboli degli elementi. E mentre la forma maiuscola è appropriata per le costanti Java, non è come normalmente scriviamo i simboli.

Inoltre, mancano anche altre proprietà degli elementi della tavola periodica, come il nome e il peso atomico.

Sebbene il tipo enum abbia un comportamento speciale in Java, possiamo aggiungere costruttori, campi e metodi come facciamo con altre classi. Per questo motivo , possiamo migliorare la nostra enum per includere i valori di cui abbiamo bisogno.

3. Aggiunta di un costruttore e di un campo finale

Cominciamo aggiungendo i nomi degli elementi. Imposteremo i nomi in una variabile finale usando un costruttore :

public enum Element { H("Hydrogen"), HE("Helium"), // ... NE("Neon"); public final String label; private Element(String label) { this.label = label; } }

Prima di tutto, notiamo la sintassi speciale nell'elenco delle dichiarazioni. Ecco come viene invocato un costruttore per i tipi enum . Sebbene sia illegale utilizzare l' operatore new per un'enumerazione , possiamo passare gli argomenti del costruttore nell'elenco delle dichiarazioni.

Dichiariamo quindi un'etichetta di variabile di istanza . Ci sono alcune cose da notare al riguardo.

In primo luogo, abbiamo scelto l' identificatore dell'etichetta invece del nome . Sebbene il nome del campo del membro sia disponibile per l'uso, scegliamo l' etichetta per evitare confusione con il metodo predefinito Enum.name () .

In secondo luogo, il nostro campo etichetta è definitivo . Sebbene i campi di un'enumerazione non debbano essere definitivi , nella maggior parte dei casi non vogliamo che le nostre etichette cambino. Nello spirito della costanza dei valori enum , questo ha senso.

Infine, il campo dell'etichetta è pubblico. Quindi, possiamo accedere direttamente all'etichetta:

System.out.println(BE.label);

D'altra parte, il campo può essere privato , accessibile con un metodo getLabel () . Per motivi di brevità, questo articolo continuerà a utilizzare lo stile del campo pubblico.

4. Individuazione dei valori Java Enum

Java fornisce un metodo valueOf (String) per tutti i tipi di enum . Pertanto, possiamo sempre ottenere un valore enum basato sul nome dichiarato:

assertSame(Element.LI, Element.valueOf("LI"));

Tuttavia, potremmo voler cercare un valore enum anche dal nostro campo etichetta. Per farlo possiamo aggiungere un metodo statico :

public static Element valueOfLabel(String label) { for (Element e : values()) { if (e.label.equals(label)) { return e; } } return null; }

Il metodo statico valueOfLabel () itera i valori di Element finché non trova una corrispondenza. Restituisce null se non viene trovata alcuna corrispondenza. Al contrario, potrebbe essere generata un'eccezione invece di restituire null .

Vediamo un rapido esempio utilizzando il nostro metodo valueOfLabel () :

assertSame(Element.LI, Element.valueOfLabel("Lithium"));

5. Memorizzazione nella cache dei valori di ricerca

Possiamo evitare di iterare i valori enum utilizzando una mappa per memorizzare nella cache le etichette . Per fare ciò, definiamo una mappa finale statica e la popoliamo quando la classe viene caricata:

public enum Element { // ... enum values private static final Map BY_LABEL = new HashMap(); static { for (Element e: values()) { BY_LABEL.put(e.label, e); } } // ... fields, constructor, methods public static Element valueOfLabel(String label) { return BY_LABEL.get(label); } }

Come risultato della memorizzazione nella cache, i valori enum vengono ripetuti solo una volta e il metodo valueOfLabel () viene semplificato.

In alternativa, possiamo costruire pigramente la cache quando vi si accede per la prima volta nel metodo valueOfLabel () . In tal caso, l'accesso alla mappa deve essere sincronizzato per evitare problemi di concorrenza.

6. Allegare più valori

Il costruttore Enum può accettare più valori . Per illustrare, aggiungiamo il numero atomico come int e il peso atomico come float :

public enum Element { H("Hydrogen", 1, 1.008f), HE("Helium", 2, 4.0026f), // ... NE("Neon", 10, 20.180f); private static final Map BY_LABEL = new HashMap(); private static final Map BY_ATOMIC_NUMBER = new HashMap(); private static final Map BY_ATOMIC_WEIGHT = new HashMap(); static { for (Element e : values()) { BY_LABEL.put(e.label, e); BY_ATOMIC_NUMBER.put(e.atomicNumber, e); BY_ATOMIC_WEIGHT.put(e.atomicWeight, e); } } public final String label; public final int atomicNumber; public final float atomicWeight; private Element(String label, int atomicNumber, float atomicWeight) { this.label = label; this.atomicNumber = atomicNumber; this.atomicWeight = atomicWeight; } public static Element valueOfLabel(String label) { return BY_LABEL.get(label); } public static Element valueOfAtomicNumber(int number) { return BY_ATOMIC_NUMBER.get(number); } public static Element valueOfAtomicWeight(float weight) { return BY_ATOMIC_WEIGHT.get(weight); } }

Allo stesso modo, possiamo aggiungere tutti i valori che vogliamo all'enumerazione , come i simboli di maiuscole e minuscole appropriati, "He", "Li" e "Be", per esempio.

Inoltre, possiamo aggiungere valori calcolati alla nostra enumerazione aggiungendo metodi per eseguire operazioni.

7. Controllo dell'interfaccia

Come risultato dell'aggiunta di campi e metodi alla nostra enum , abbiamo modificato la sua interfaccia pubblica. Pertanto, il nostro codice, che utilizza i metodi principali Enum name () e valueOf () , non sarà a conoscenza dei nostri nuovi campi.

Il metodo statico valueOf () è già definito per noi dal linguaggio Java. Pertanto, non possiamo fornire la nostra implementazione valueOf () .

Allo stesso modo, poiché il metodo Enum.name () è definitivo, non possiamo nemmeno sovrascriverlo .

Di conseguenza, non esiste un modo pratico per utilizzare i nostri campi aggiuntivi utilizzando l' API Enum standard . Invece, diamo un'occhiata ad alcuni modi diversi per esporre i nostri campi.

7.1. Sostituzione di toString ()

L'override di toString () può essere un'alternativa all'override di name () :

@Override public String toString() { return this.label; }

Per impostazione predefinita, Enum.toString () restituisce lo stesso valore di Enum.name ().

7.2. Implementazione di un'interfaccia

Il tipo enum in Java può implementare le interfacce . Sebbene questo approccio non sia generico come l' API Enum , le interfacce ci aiutano a generalizzare.

Consideriamo questa interfaccia:

public interface Labeled { String label(); }

Per coerenza con il metodo Enum.name () , il nostro metodo label () non ha un prefisso get .

Inoltre , poiché il metodo valueOfLabel () è statico , non lo includiamo nella nostra interfaccia.

Infine, possiamo implementare l'interfaccia nella nostra enum :

public enum Element implements Labeled { // ... @Override public String label() { return label; } // ... }

Un vantaggio di questo approccio è che l' interfaccia Labeled può essere applicata a qualsiasi classe, non solo ai tipi enum . Invece di fare affidamento sull'API Enum generica , ora abbiamo un'API più specifica per il contesto.

8. Conclusione

In questo articolo, abbiamo esplorato molte funzionalità dell'implementazione di Java Enum . Aggiungendo costruttori, campi e metodi, vediamo che enum può fare molto di più delle costanti letterali.

Come sempre, il codice sorgente completo di questo articolo può essere trovato su GitHub.