2013-03-31 17 views
47

Stavo cercando di capire come funziona davvero enum Java e sono giunto alla conclusione che è molto simile a una normale classe Java che ha dichiarato privato il costruttore.Quali sono le differenze tra un enum Java e una classe con il costruttore privato?

Sono appena arrivato a questa conclusione e non si basa su molto pensare, ma mi piacerebbe sapere se mi manca qualcosa.

Quindi di seguito è un'implementazione di un semplice enum Java e una classe Java equivalente.

public enum Direction { 
    ENUM_UP(0, -1), 
    ENUM_DOWN(0, 1), 
    ENUM_RIGHT(1, 0), 
    ENUM_LEFT(-1, 0); 


    private int x; 
    private int y; 

    private Direction(int x, int y){ 
     this.x = x; 
     this.y = y; 
    } 
    public int getEnumX(){ 
     return x; 
    } 
    public int getEnumY(){ 
     return y; 
    } 
} 

Qual è la differenza di significato tra il codice sopra e sotto?

public class Direction{ 
    public static final Direction UP = new Direction(0, -1) ; 
    public static final Direction DOWN = new Direction(0, 1) ; 
    public static final Direction LEFT = new Direction(-1, 0) ; 
    public static final Direction RIGHT = new Direction(1, 0) ; 


    private int x ; 
    private int y ; 

    private Direction(int x, int y){ 
     this.x = x ; 
     this.y = y ; 
    } 
    public int getX(){ 
     return x; 
    } 
    public int getY(){ 
     return y; 
    } 
} 
+7

Buona domanda, sono davvero simili in molti modi :) Con molte differenze anche io sono sicuro che le buone risposte qui delineeranno. –

+2

Almeno tre differenze a cui posso pensare: [1] non sarai in grado di passare a quest'ultima implementazione (es. 'Direzione d; interruttore (d)'), [2] 'Direzione.toString()' le implementazioni saranno diverse, [3] Il metodo enum consente di ottenere tutte le istanze della "classe" tramite '.values ​​()' considerando che il metodo di classe non lo fa. – jedwards

+2

Perderai 'values ​​()', 'valueOf()' e 'ordinal()'. –

risposta

46

Differenze:

  1. enumerazioni estendono java.lang.Enum e acquisire tutta la sua nice features:
    1. comportamento automatico Singleton attraverso la corretta serializzazione
    2. automatico leggibile .toString metodo su valori enum senza la necessità di duplicare il vostro enum names
    3. .name e .ordinal metodi per usi speciali
    4. Utilizzabile ad alte prestazioni BitSet a base EnumSet e EnumMap classi
  2. enumerazioni sono trattati dal linguaggio appositamente:
    1. enumerazioni utilizzare una sintassi speciale che semplifica la creazione dell'istanza senza scrivere decine di public static final campi
    2. Le enumerazioni possono essere utilizzate nelle istruzioni switch
    3. Le enumerazioni non possono essere istanziate al di fuori dell'elenco enumerazione se non utilizzando la riflessione
    4. Enums non può essere esteso al di fuori della lista di enumerazione
  3. Java compila automaticamente materiale extra in enumerazioni:
    1. public static (Enum)[] values();
    2. public static (Enum) valueOf(java.lang.String);
    3. private static final (Enum)[] $VALUES; (values() restituisce un clone di questa)

La maggior parte di questi può essere emulata con una classe opportunamente progettata, ma con lo Enum, è davvero facile creare una classe con questo insieme di proprietà particolarmente desiderabili.

+0

come funziona il singleton? per ogni valore ha un singleton? cosa succede se crei un enum che ha setter e getter? utilizzare i setter su una singola variabile enum influisce su altre variabili con lo stesso valore? –

+0

Ogni valore Enum è un singleton, quindi sì, l'utilizzo di un setter su uno influirà su tutte le variabili che fanno riferimento a quel valore. Pertanto, si consiglia vivamente di non avere mai uno stato mutabile nel proprio Enum. – nneonneo

+0

Vedo. sembra logico. Grazie. –

6

Date un'occhiata a this blogpage, si descrive come Java enum s vengono compilati in bytecode. Vedrai che c'è una piccola aggiunta rispetto al tuo secondo esempio di codice, che è un array di oggetti Direction chiamato VALUES. Questo array contiene tutti i valori possibili per la vostra enum, in modo da non essere in grado di fare

new Direction(2, 2) 

(ad esempio utilizzando la riflessione) e quindi utilizzare che come un valore valido Direction.

Inoltre, come @ Eng.Fouad spiega correttamente, non si dispone di values(), valueOf() e ordinal().

+1

So che puoi * modificare * il costruttore privato della classe 'Direction' usando il reflection e creare nuove istanze di esso, ma non sono sicuro che tu possa fare lo stesso con l'enumerazione. –

+1

In base al modo in cui vengono compilati in bytecode, potresti essere in grado di farlo. Non ho mai provato me stesso, interessante esperimento però ... – mthmulders

0

La differenza principale è che ciascuna classe enum estende implicitamente la classe Enum<E extends Enum<E>>. Ciò porta a che:

  1. enum oggetti hanno metodi che name() e ordinal()
  2. enum oggetti hanno particolari toString(), hashCode(), equals() e compareTo() implementazioni
  3. enum oggetti sono adatti per switch operatore.

Tutto quanto sopra menzionato non è applicabile per la vostra versione di classe Direction. Questa è la differenza di "significato".

6

Per rispondere alla domanda: in sostanza, non c'è differenza tra i due approcci. Tuttavia, enum construct fornisce alcuni metodi di supporto aggiuntivi come values(), valueOf(), ecc. Che dovresti scrivere da solo con l'approccio class-with-private-constructor.

Ma sì, mi piace come le enumerazioni Java siano per lo più come qualsiasi altra classe in Java, possono avere campi, comportamenti, ecc. Ma per me ciò che separa le enumerazioni dalle classi semplici è l'idea che le enumerazioni sono classi/tipi le cui istanze/membri sono predeterminati. A differenza delle solite classi in cui è possibile creare un numero qualsiasi di istanze, le enumerazioni limitano solo la creazione a istanze note. Sì, come hai illustrato, puoi farlo anche con classi con costruttori privati, ma le enumerazioni lo rendono più intuitivo.

4

Come le persone hanno sottolineato si perde values(), valueOf() e ordinal().È possibile replicare questo comportamento abbastanza facilmente utilizzando una combinazione di uno Map e uno List.

public class Direction { 

    public static final Direction UP = build("UP", 0, -1); 
    public static final Direction DOWN = build("DOWN", 0, 1); 
    public static final Direction LEFT = build("LEFT", -1, 0); 
    public static final Direction RIGHT = build("RIGHT", 1, 0); 
    private static final Map<String, Direction> VALUES_MAP = new LinkedHashMap<>(); 
    private static final List<Direction> VALUES_LIST = new ArrayList<>(); 
    private final int x; 
    private final int y; 
    private final String name; 

    public Direction(int x, int y, String name) { 
     this.x = x; 
     this.y = y; 
     this.name = name; 
    } 

    private static Direction build(final String name, final int x, final int y) { 
     final Direction direction = new Direction(x, y, name); 
     VALUES_MAP.put(name, direction); 
     VALUES_LIST.add(direction); 
     return direction; 
    } 

    public int getX() { 
     return x; 
    } 

    public int getY() { 
     return y; 
    } 

    public static Direction[] values() { 
     return VALUES_LIST.toArray(new Direction[VALUES_LIST.size()]); 
    } 

    public static Direction valueOf(final String direction) { 
     if (direction == null) { 
      throw new NullPointerException(); 
     } 
     final Direction dir = VALUES_MAP.get(direction); 
     if (dir == null) { 
      throw new IllegalArgumentException(); 
     } 
     return dir; 
    } 

    public int ordinal() { 
     return VALUES_LIST.indexOf(this); 
    } 

    @Override 
    public int hashCode() { 
     int hash = 7; 
     hash = 29 * hash + name.hashCode(); 
     return hash; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
      return false; 
     } 
     if (getClass() != obj.getClass()) { 
      return false; 
     } 
     final Direction other = (Direction) obj; 
     return name.equals(other.name); 
    } 

    @Override 
    public String toString() { 
     return name; 
    } 
} 

Come potete vedere; il codice diventa molto goffo molto rapidamente.

Non sono sicuro se esiste un modo per replicare un'istruzione switch con questa classe; quindi lo perderai.

Problemi correlati