2010-09-29 17 views
11

Sto tentando di trovare un modo per forzare Java a caricare/inizializzare un tipo enumerato (che è annidato in una classe che contiene una mappa statica).Forza inizializzazione di un tipo enumerato in Java

Questo è importante per me perché il tipo enumerato ha un costruttore che popola detta mappa, e senza un modo esplicito per inizializzare questa enumerazione, la mappa rimarrà vuota. Ho tentato di utilizzare Class.forName, ma questo non sembra funzionare.

Suppongo di poter creare un'istanza dell'enumerazione (e memorizzarla in un'altra raccolta o qualcosa del genere), ma mi piacerebbe sapere se c'è un modo elegante per farlo.

+2

Se non sei garantito l'enumerazione sarà costruito prima è necessario utilizzare la mappa, si potrebbe voler riconsiderare il vostro disegno qui, anche se la raccomandazione di Matt di utilizzare un inizializzatore statico sarà probabilmente ottenere tu quello che vuoi – Bryan

+1

'Class.forName' inizializza una classe. Se "non funziona", il codice ha altri problemi che non ti rendi conto. Perché non pubblicare il tuo codice. – irreputable

+0

Fondamentalmente vedo un'eccezione che indica che la classe è "non valida" e so per certo che ho il percorso del pacchetto corretto. è Class.forName() * supposto * che funzioni sull'enumerazione in tutti i casi? –

risposta

10

Una classe viene caricata quando si fa riferimento a una classe. Funziona allo stesso modo per tutte le classi.

Il problema riscontrato è più probabile che un valore Enum sia inizializzato prima di qualsiasi blocco statico. Ad esempio, non è possibile fare riferimento a qualcosa di inizializzato in un blocco statico in un costruttore. (Generalmente inizializzare il contenuto statico in un costruttore è un'idea BAD) È necessario inizializzare la mappa nel blocco statico, non nel costruttore.

Prova

import java.util.Map; 
import java.util.HashMap; 

public enum EnumTest { 
    FOO, BAR, BAZ; 

    private static final Map<String,EnumTest> map = new LinkedHashMap<String,EnumTest>(); 
    static { 
     for(EnumTest e : EnumTest.values()) 
     map.put(e.name(), e); 
    } 

    public static void main(String... args) { 
    System.out.println(EnumTest.map); 
    } 
} 
+0

Questo sembra l'approccio migliore finora. Darò un colpo. –

+1

Btw +1 a trashgod come ha sottolineato un esempio che utilizza questo stesso approccio. –

+0

Ha funzionato. E sono d'accordo con la tua affermazione che "l'inizializzazione del contenuto statico in un costruttore è un'idea BAD". Sono andato avanti con questo approccio. –

5

Non è possibile inserire l'inizializzazione della mappa nell'inizializzatore statico del tipo Enum?

public enum SomeEnum 
{ 
    Member1, Member2, Member3 ... 

    private static Map<K, SomeEnum> map = ...; 
    static 
    { 
     ...populate map... 
    } 
    ... 

EDIT: Sembra che il problema fosse che le definizioni dei membri Enum devono venire prima. Immagino di averlo semplicemente sorvolato. Ho risolto l'esempio.

+0

Questo non sembra risolvere il problema poiché tutte le istanze create all'interno di SomeEnum sono comunque statiche. Il problema rimane ancora: come faccio a dire al classloader di inizializzare/caricare un enum? Una volta risolto questo, tutto il mio codice statico per l'Enum dovrebbe essere eseguito secondo le necessità. –

+1

Allora puoi approfondire un po '? Che problema sta causando? Come si può accedere alla mappa prima che Enum sia inizializzato se Enum esegue l'inizializzazione? La mappa NON è contenuta nell'Enum? –

+0

corretto. La mappa non è contenuta all'interno dell'enum. E non riesco a posizionare la mappa all'interno dell'enumerazione poiché Java non consente a un costruttore enum di compilare una raccolta statica che si trova all'interno di una enumerazione. –

3

È sufficiente fare riferimento a qualcosa nella classe enum. Per esempio:

public class EnumTest { 
    static final Map<String, MyEnum> map = new HashMap<String, MyEnum>(); 

    enum MyEnum { 
    FOO, BAR, BAZ; 

    MyEnum() { 
     map.put(name(), this); 
    } 
    } 
    static { 
    // ensure MyEnum is initialized 
    MyEnum.values(); 
    } 

    public static void main(String[] argsa) { 
    System.out.println(map.size()); 
    } 
} 
+0

Questa è la soluzione di base che sto utilizzando. Ma dal momento che il valore restituito da MyEnum.values ​​() non è effettivamente utilizzato nel codice, sono preoccupato che forse sarà "ottimizzato". Poi di nuovo, ho visto un trucco simile usato per garantire che una classe regolare sia inizializzata, quindi forse questo è un modo decente per risolvere il problema. –

+1

sono abbastanza sicuro che non può essere completamente ottimizzata con precisione a causa del fatto che può potenzialmente causare un'inizializzazione classe. Java lascia molto meno "indefinito" di C o C++, nel bene e nel male. –

2

Sembra che questo è esattamente il motivo per cui è spesso consigliato di utilizzare metodi di accesso al posto dei membri che fanno riferimento direttamente. Il tuo problema è che il codice consente l'accesso alla mappa prima che sia inizializzata. Blocca l'accesso arbitrario alla mappa e nascondilo dietro un metodo accessor che si assicura che sia inizializzato.

import java.util.Map; 
import java.util.HashMap; 

public enum EnumTest { 
    FOO, BAR, BAZ; 

    private static Map<String,EnumTest> map = null; 

    public synchronized static Map getMap() { 
    if (map == null) { 
     map = new HashMap<String,EnumTest>(); 
     for (EnumTest e : EnumTest.values()) { 
     map.put(e.name(), e); 
     } 
    } 

    return map; 
    } 

    public static void main(String[] args) { 
    System.out.println(EnumTest.getMap().size()); 
    } 
} 
+0

+1 per l'approvazione dell'incapsulamento qui. –

Problemi correlati