2013-01-18 14 views
5

Ho creato un Enum per definire determinate azioni. Programmazione contro un'API esterna Sono costretto a utilizzare un Integer per esprimere questa azione. Ecco perché ho aggiunto un campo istanza intero al mio Enum. Questo dovrebbe essere d'accordo con Effective Java di Joshua Bloch, invece di fare affidamento su ordinal() o sull'ordine delle costanti Enum usando values()[index].Creare un metodo factory enum con un valore di istanza univoco

public enum Action { 

    START(0), 

    QUIT(1); 

    public final int code; 

    Protocol(int code) { 
     this.code = code; 
    } 
} 

ottengo un valore intero what dalle API e ora voglio per creare un valore Enum fuori di esso, come posso implementare questo nel modo più generica?

Ovviamente, l'aggiunta di un tale metodo di fabbrica, non funzionerà. Non puoi istanziare un Enum.

Action valueOf(int what) { 
    return new Action(what); 
} 

Naturalmente, posso sempre fare un'istruzione switch-case e aggiungere tutti i possibili codici e restituire la costante appropriata. Ma voglio evitare di definirli in due posti allo stesso tempo.

risposta

5

Se avete intenzione di avere un sacco di loro, è possibile utilizzare un HashMap<Integer, Action>:

private static final Map<Integer, Action> actions = new HashMap<>(values().size, 1); 

static { 
    for (Action action : values()) 
     actions.put(action.code, action); 
} 

// ... 

public static Action valueOf(int what) { 
    return actions.get(what); 
} 

Ciò è utile se si sta andando ad avere un gran numero di Action valori dal momento che la ricerca è HashMap O (1).

+0

È possibile inizializzare la mappa con le dimensioni corrette: 'nuova HashMap <> (Action.values ​​(). Length, 1);' (non farà differenza se non ci sono molti valori ma non costa qualsiasi cosa lo faccia). – assylias

+0

@assylias Divertente, ho esaminato il codice sorgente e il costruttore ignora effettivamente il fattore di carico: * Si noti che questa implementazione ignora loadFactor; usa sempre un fattore di carico di 3/4. Questo semplifica il codice e in generale migliora le prestazioni. * –

+0

Non lo sapevo, interessante. – assylias

1

Se si è sicuri che i vostri codici saranno sempre sequenziale e partendo da 0, allora l'opzione più efficiente sarebbe

public enum Action { 
    START(0), 

    QUIT(1); 

    public static final Action[] ACTIONS; 
    static { 
     ACTIONS = new Action[values().length]; 
     for(Action a : values()) { 
     ACTIONS[a.code] = a; 
     } 
    } 

    public final int code; 

    Protocol(int code) { 
     this.code = code; 
    } 
} 
0

Io personalmente mantenere le cose semplici (YAGNI) e utilizzare il valore ordinale ma :

  • vorrei mantenere la logica all'interno del enum per assicurarsi che il codice di fuori non sa su questo dettaglio di implementazione e non si basa su di esso
  • I wou Assicurati di avere un test che fallisce se qualcosa si rompe (ad es. se i numeri non partono da 0 o non sono incrementali)

codice enum:

public enum Action { 

    START(0), 
    QUIT(1); 
    private final int code; 

    Action(int code) { 
     this.code = code; 
    } 

    public int getCode() { 
     return code; 
    } 

    public static Action of(int code) { 
     try { 
      return Action.values()[code]; 
     } catch (IndexOutOfBoundsException e) { 
      throw new IllegalArgumentException("not a valid code: " + code); 
     } 
    } 
} 

prova

@Test 
public testActionEnumOrder() { 
    int i = 0; 
    for (Action a : Action.values()) { 
     assertEquals(a.getCode(), i++); 
    } 
} 

Se si cambia QUIT(1)-QUIT(2) per esempio , il test fallirà. Quando ciò accade, puoi usare una HashMap o un ciclo di ricerca.

+0

Interessante. Non vorrei favorire l'utilizzo di codice che è più probabile che si interrompa a causa di modifiche al codice laddove ciò non è vero. Ma non ho pensato a scegliere il precedente e a sostenerlo con un caso di test. –

+0

@MaxRhan Se si teme che il test non possa essere eseguito, è possibile includere anche il controllo in un blocco di inizializzazione statico in modo che venga eseguito ogni volta che la classe viene caricata. Qualunque cosa rompa il design verrà avvistata molto presto. – assylias

Problemi correlati