2009-07-22 13 views
59

En Java, se puede crear una enumeración de la siguiente manera:¿Cómo se implementa values ​​() para enums de Java 6?

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

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

Esta pregunta se refiere a los "valores) (" método. Específicamente, ¿cómo se implementa? Por lo general, podría saltar a la fuente para las clases de Java usando F3 o CTRL + clic en Eclipse (incluso para clases como Cadena, Carácter, Entero e incluso Enum). Es posible ver el origen de los otros métodos enum (por ejemplo, valueOf (String)).

¿"values ​​()" crea una nueva matriz cada vez que se invoca? Si lo asigno a una variable local y luego modifico uno de los elementos, ¿qué ocurre? (Claramente esto no afectará el valor devuelto por los valores(), lo que implica que se asigna una nueva matriz cada vez).

¿Es el código nativo? O el compilador/JVM lo trata especialmente, solo devuelve una nueva instancia de values ​​() cuando no puede probar que no se modificará.

Respuesta

96

Básicamente, el compilador (javac) traduce su enumeración en una matriz estática que contiene todos sus valores en tiempo de compilación. Cuando llama a values ​​(), le da una copia .clone'd() de esta matriz.

Dada esta simple enumeración:

public enum Stuff { 
    COW, POTATO, MOUSE; 
} 

En realidad se puede mirar el código que 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); 
    } 
} 

Puede buscar la forma javac 'traduce' sus clases haciendo un directorio temporal y en ejecución:

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

¿No sería más rápido un System.arraycopy? – Gandalf

+0

+1; felicitaciones adicionales si explicas el significado de 'sintético'. – wchargin

+3

¿Por qué 'values ​​()' method 'clone()' the array '$ VALUES'? ¿Por qué no solo devolverlo directamente? – RestInPeace

2

Si lo asigna a una variable local el Lo único que puede modificar es asignar otra enumeración a esta variable. Esto no cambiará la enumeración en sí porque solo está cambiando el objeto al que hace referencia su variable.

Parece que las enumeraciones son, de hecho, singletons, por lo que solo un elemento de cada enumeración puede existir en todo el programa, esto hace que el operador == sea legal para las enumeraciones.

Por lo tanto, no hay ningún problema de rendimiento y no puede cambiar accidentalmente algo en su definición de enumeración.

+2

creo que OP significa modificar la matriz devuelta por los valores(). si este es el mismo objeto de matriz que se guarda internamente (en lugar de una copia), entonces modificarlo (por ejemplo, asignar un elemento a otro, asignar nulo a un elemento, etc.) lo arruinaría, no solo para la clase enum, pero para futuras llamadas a valores() – newacct

+1

Sí, tiene razón. Si no hubiera un clon, podría eliminar un Enum de la matriz y las llamadas posteriores de los valores perderían este valor. – Janusz

+0

Y, sin embargo, es solo una copia superficial, ya que las instancias enum son únicas. –

1

¿El código es nativo? O el compilador/JVM lo trata especialmente, solo devuelve una nueva instancia de values ​​() cuando no puede probar que no se modificará.

1) No. O al menos no en las implementaciones actuales. Ver la respuesta de @lucasmo para la evidencia.

2) AFAIK, no.

Hipotéticamente podría hacer esto. Sin embargo, demostrar que una matriz nunca se modifica localmente sería complicado y relativamente costoso para el JIT. Si la matriz "escapa" del método que llamó a values(), se vuelve más complejo & más caro.

Lo más probable es que esta optimización (hipotética) no rinda ... cuando se promedia sobre todo el código de Java.

El otro problema es que esta optimización (hipotética) podría abrir agujeros de seguridad.


Lo interesante es que aunque el JLS no parece indicar que el miembro values() devuelve una copia matriz. El sentido común dice que debe hacer ... pero en realidad no está especificado.

1 - Sería un agujero de seguridad enorme si values() devolviera una matriz compartida (mutable) de valores enum.