2012-03-08 14 views
9

Cuando estoy leyendo el "java efectivo", el autor me dijo que un tipo de enumeración de elemento único es la mejor manera de implementar un singleton, porque no tenemos que considerar sofisticados ataques de serialización o reflexión . Esto significa que no podemos crear una instancia de enum usando la reflexión, ¿es así? He hecho algunas pruebas, una clase de enumeración aquí:¿Cómo crear una instancia de enum usando reflection en java?

public enum Weekday {} 

Luego trató de crear una instancia del Día de la semana:

Class<Weekday> weekdayClass = Weekday.class; 
    Constructor<Weekday> cw = weekdayClass.getConstructor(null); 
    cw.setAccessible(true); 
    cw.newInstance(null); 

Como saben, no funciona. Cuando cambio la palabra clave "enum" por "clase", funciona. Quiero saber por qué. Gracias pre.

+0

Puede crear una clase enum sobre la marcha utilizando la generación de código byte. Pero en ese caso, la enumeración no existiría en tiempo de compilación, por lo que no sería capaz de referirse a ella en el resto de su código (solo podría acceder a ella a través de la reflexión, que es una ejecución -tiempo y no un mecanismo de tiempo de compilación). –

Respuesta

17

Esto está integrado en el idioma. De Java Language Specification (§8.9):

Es un error en tiempo de compilación intentar instanciar explícitamente un tipo de enumeración (§15.9.1). El método de clonación final en Enum garantiza que las constantes de la enumeración nunca se puedan clonar, y el tratamiento especial del mecanismo de serialización garantiza que las instancias duplicadas nunca se creen como resultado de la deserialización. La ejemplificación reflexiva de los tipos enum está prohibida. En conjunto, estas cuatro cosas aseguran que no existan instancias de un tipo de enumeración más allá de las definidas por las constantes enum.

Todo el propósito de esto es permitir el uso seguro de == para comparar Enum instancias.

+0

gracias, lo sé claro. –

+0

El 'enum' es en realidad compilado en un' class' final que se extiende 'java.lang.Enum' con un constructor de' private', y eso no impide en realidad de instancias de reflexión si 'setAccessible (true);' se llama. Todavía me pregunto cómo se previene eso? –

+1

@ AhmadY.Saleh - Ver [la respuesta por soc] (http://stackoverflow.com/a/14432647/535871). –

0

Enums ha sido diseñado para tratarse como objetos constantes. Reemplaza a readObject y arroja una excepción de objeto no válido para evitar la serialización predeterminada. También anula clone() y lanza la excepción clon no soportado. En lo que respecta a la reflexión, el constructor de Enum está protegido. Así que si usas el código anterior, arrojará NoSuchMethodFound.

Incluso si usa getDeclaredConstructor() en lugar de getConstructor, debería obtener la misma excepción. Supongo que ha sido restringido a través de SecurityManager en Java.

1

Es correcto que las nuevas instancias de una clase enum no puedan crearse retroactivamente, ni siquiera con reflexión.

El código siguiente muestra esto:

val weekdayClass = classOf[Weekday] 
val weekdayConstructor = weekdayClass getDeclaredConstructor (classOf[String], classOf[Int]) 
weekdayConstructor setAccessible true 
weekdayConstructor newInstance ("", Integer.valueOf(0)) 

Por lo general, esto debería funcionar. Pero en el caso de las enumeraciones, esto es especial con carcasa en Constructor#newInstance:

if ((clazz.getModifiers() & Modifier.ENUM) != 0) 
    throw new IllegalArgumentException("Cannot reflectively create enum objects"); 

Por lo tanto, recibirá la siguiente excepción cuando se trata de una nueva instancia de enumeración:

java.lang.IllegalArgumentException: Cannot reflectively create enum objects 
     at java.lang.reflect.Constructor.newInstance(Constructor.java:520) 
     ... 

Asumo que la última enfoque (que probablemente será exitoso, porque no se ejecutan cheques o constructores) implica sun.misc.Unsafe#allocateInstance.

0

Si su objetivo es persistente y luego reconstruir la información de la enumeración. Necesitarás persistir enumClassName y su valor.

public enum DaysOfWeek{ Mon, Tue, Wed, Thu, Fri, Sat, Sun } 

DaysOfWeek dow = DaysOfWeek.Tue; 
String value = dow.toString(); 
String enumClassName = dow.getClass().getName(); 

// Persist value and enumClassName 
// ... 

// Reconstitute the data 
Class clz = Class.forName(enumClassName); 
Object o = Enum.valueOf(clz, value); 
DaysOfWeek dow2 = (DaysOfWeek)o; 
System.out.println(dow2); 
4

Esto puede estar reviviendo un post muerto, pero se puede obtener una instancia de cada constante declara usando Weekday.class.getEnumConstants(). Esto devuelve una matriz de todos los constantes, donde obtener una sola instancia es trivial, getEnumConstants()[0].

Cuestiones relacionadas