2009-07-22 7 views
59

In Java, è possibile creare un enum come segue:In che modo values ​​() è implementato per le enumerazioni di Java 6?

public enum Letter { 
    A, B, C, D, E, F, G; 

    static { 
     for(Letter letter : values()) { 
      // do something with letter 
     } 
    } 
} 

Tale questione riguarda il metodo dei ") (valori". In particolare, come viene implementato? Di solito, potrei passare al sorgente per le classi Java usando F3 o CTRL + Fare clic su Eclipse (anche per classi come String, Character, Integer e anche Enum). È possibile visualizzare l'origine degli altri metodi enum (ad es. ValueOf (String)).

Does "values ​​()" crea un nuovo array ogni volta che viene richiamato? Se lo assegno ad una variabile locale e poi modifico uno degli elementi, cosa succede (chiaramente questo non influenzerà il valore restituito da values ​​(), il che implica che ogni volta viene assegnato un nuovo array).

Il codice è nativo? Oppure la JVM/compilatore lo tratta in modo speciale, restituendo solo una nuova istanza da values ​​() quando non può dimostrare che non verrà modificata.

risposta

96

Fondamentalmente, il compilatore (javac) traduce il tuo enum in un array statico contenente tutti i tuoi valori in fase di compilazione. Quando chiami values ​​(), ti dà una copia di .clone'd() di questo array.

Dato questo semplice enum:

public enum Stuff { 
    COW, POTATO, MOUSE; 
} 

Si può effettivamente guardare il codice che Java genera:

public enum Stuff extends Enum<Stuff> { 
    /*public static final*/ COW /* = new Stuff("COW", 0) */, 
    /*public static final*/ POTATO /* = new Stuff("POTATO", 1) */, 
    /*public static final*/ MOUSE /* = new Stuff("MOUSE", 2) */; 
    /*synthetic*/ private static final Stuff[] $VALUES = new Stuff[]{Stuff.COW, Stuff.POTATO, Stuff.MOUSE}; 

    public static Stuff[] values() { 
     return (Stuff[])$VALUES.clone(); 
    } 

    public static Stuff valueOf(String name) { 
     return (Stuff)Enum.valueOf(Stuff.class, name); 
    } 

    private Stuff(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) { 
     super($enum$name, $enum$ordinal); 
    } 
} 

Potete guardare come javac 'traduce' le vostre classi facendo una directory temporanea e in esecuzione:

javac -d <output directory> -XD-printflat filename.java 
+1

Non sarebbe un System.arraycopy essere più veloce? – Gandalf

+0

+1; complimenti extra se spiegherai il significato di 'sintetico'. – wchargin

+3

Perché 'values ​​()' metodo 'clone()' l'array '$ VALUES'? Perché non restituirlo direttamente? – RestInPeace

2

Se lo si assegna a una variabile locale il l'unica cosa che puoi modificare è assegnare un altro enum a questa variabile. Questo non cambierà l'enum stesso perché stai solo cambiando l'oggetto che la tua variabile fa riferimento.

Sembra che le enumerazioni siano in effetti singleton in modo che solo un elemento di ogni enumerazione possa esistere in tutto il programma, questo rende l'operatore == legale per enumerazioni.

Quindi non ci sono problemi di prestazioni e non è possibile modificare accidentalmente qualcosa nella definizione enum.

+2

penso che l'OP significhi modificare l'array restituito da values ​​(). se questo è lo stesso oggetto dell'array che è mantenuto internamente (invece di una copia), modificarlo (ad esempio assegnando un elemento a un altro, assegnando null a un elemento, ecc.) lo farebbe male, non solo per la classe enum, ma per eventuali future chiamate a valori() – newacct

+1

Sì hai ragione. Se non esisteva un clone, è possibile rimuovere un Enum dall'array e le chiamate successive dai valori perderebbero questo valore. – Janusz

+0

Eppure è solo una copia superficiale, dal momento che le istanze enum sono uniche. –

1

Il codice è nativo? Oppure la JVM/compilatore lo tratta in modo speciale, restituendo solo una nuova istanza da values ​​() quando non può dimostrare che non verrà modificata.

1) No. O almeno non nelle attuali implementazioni. Vedi la risposta di @ lucasmo per le prove.

2) AFAIK, n.

Ipoteticamente potrebbe farlo. Tuttavia, dimostrare che un array non viene mai modificato localmente sarebbe complicato e relativamente costoso da eseguire per il JIT. Se l'array "fugge" dal metodo che ha chiamato values(), diventa più complesso & più costoso.

È probabile che questa ottimizzazione (ipotetica) non ripaghi ... quando viene calcolata la media su tutto il codice Java.

L'altro problema è che questa ottimizzazione (ipotetica) potrebbe aprire buchi di sicurezza.


La cosa interessante è però che la JLS non sembra indicare che il membro values() restituisce una copia array. Il buon senso dice che è deve fare ... ma in realtà non è specificato.

1 - Sarebbe un buco di sicurezza spalancato se values() restituisce un array condiviso (mutabile) di valori enum.

Problemi correlati