2010-03-30 15 views
18

He comenzado un Enume bastante grande llamado Descriptores que he querido utilizar como una lista de referencia en mi modelo. Pero ahora me encontré con un límite de compilador/máquina virtual la primera vez, así que estoy buscando la mejor solución para manejar esto.Enum excediendo el límite de 65535 bytes del inicializador estático ... ¿qué es lo mejor?

Aquí está mi error: El código para el inicializador estático es superior a los 65.535 bytes limitan

Está claro de dónde viene - mi Enum simplemente tiene lejos de mucho elementos. Pero necesito esos elementos, no hay forma de reducir ese conjunto.

Initialy He planificado utilizar un solo Enum porque quiero asegurarme de que todos los elementos dentro de Enum son únicos. Se utiliza en un contexto de persistencia Hibernate donde la referencia a Enum se almacena como valor de cadena en la base de datos. ¡Así que esto debe ser único!

El contenido de mi Enum se puede dividir en varios grupos de elementos que pertenecen. Pero dividir el Enum eliminaría la seguridad única que obtengo durante el tiempo de compilación. ¿O se puede lograr esto con múltiples Enumeces de alguna manera?

Mi única idea actual es definir alguna Interfaz llamada Descriptor y codificar varios Enums para implementarla. De esta forma, espero poder utilizar el mapeo Enum de Hibernate como si fuera un solo Enum. Pero ni siquiera estoy seguro de si esto funcionará. Y pierdo seguridad única.

¿Alguna idea de cómo manejar ese caso?

+0

Véase mi respuesta a esta pregunta: http://stackoverflow.com/a/27515185/768795 –

Respuesta

0

Mi idea original era para mapear la enumeración utilizando el annotion @Enumerated. Esto habría parecido al siguiente ejemplo:

@Enumerated(STRING) 
private DescriptorEnum descriptor; 

La base de datos tendría una columna llamada DESCRIPTOR de tipo VARCHAR y Hibernate (en mi caso) sería un mapa de la cadena a la enumeración.

Pero tengo ese límite de 65k (ver pregunta) que es pequeño en mi caso. Pero he encontrado una solución. Eche un vistazo al siguiente ejemplo:

public final class Descriptor { 
    public final String acronym; 
    private static final Hashtable<String, Descriptor> all = new Hashtable<String, Descriptor>(); 
    static { 
     initialize(); 
    } 
    private static void initialize() { 
     new Descriptor("example001"); 
     new Descriptor("example002"); 
     new Descriptor("example003"); 
    } 
    private Descriptor(String acronym) { 
     this.acronym = acronym; 
     if (all.contains(this.acronym)) { 
      throw new RuntimeException("duplicate acronym: " + this.acronym); 
     } 
     all.put(this.acronym, this); 
    } 
    public static Descriptor valueOf(String acronym) { 
     return all.get(acronym); 
    } 
    public String value() { 
     return this.acronym; 
    } 
} 

Esta clase de descriptor simula el uso de una enumeración típica.Pero ahora puedo dividir el método initialize() en varios que funcionan alrededor del límite de 65k que también existe para los métodos. Enum no permite dividir la inicialización en varios fragmentos, mi clase sí.

ahora tengo que usar un mapeo ligeramente diferente, sin embargo:

@Column(name = "DESCRIPTOR") 
private String     descriptorAcronym  = null; 
private transient Descriptor descriptor    = null; 
public Descriptor getDescriptor() { 
    return descriptor; 
} 
public void setDescriptor(Descriptor desc) { 
    this.descriptor = desc; 
    this.descriptorAcronym = desc != null ? desc.acronym : null; 
} 
public String getDescriptorAcronym() { 
    return descriptorAcronym; 
} 
public void setDescriptorAcronym(String desc) { 
    this.descriptorAcronym = desc; 
    this.descriptor = desc != null ? Descriptor.valueOf(desc) : null; 
} 
@PostLoad 
private void syncDescriptor() { 
    this.descriptor = this.descriptorAcronym != null ? Descriptor.valueOf(this.descriptorAcronym) : null; 
} 

De esta manera puedo utilizar la clase como una enumeración en la mayoría de los casos. Es un poco complicado ... pero parece funcionar. Gracias por toda la información que finalmente me llevó a esa solución.

3

Puede probar el typesafe enum pattern descrito por Joshua Bloch en la primera edición de su libro "Effective Java".

La idea es crear una clase con un constructor privado. Dentro de esta clase, usted define instancias estáticas de cualquier número que desee, igual que en una enumeración Java.

No sé si hay un límite para el número de miembros estáticos en el lenguaje Java, sin embargo.

+0

Eso es exactamente lo que parece ser. El error indica un límite del código de inicializador estático ... no la enumeración en sí. Pero investigaré esto un poco ... gracias. –

+0

Entonces tal vez tenga que crear un conjunto fijo de instancias que no sean estáticas. Sin embargo, eso haría imposible usar una constante como Suite.HEARTS directamente. Su requerimiento parece solicitar una solución bastante inusual. – migu

+0

Gracias por su contribución aquí ... He llegado a una solución similar a la descrita por ese patrón. –

8

Simple. No use enum para esto. No puedes. No funcionará

Lo más probable es que su código fuente no se refiera explícitamente a muchos de los valores enum. Más bien, está usando la enumeración como una forma conveniente de mapear entre instancias de objetos únicos y nombres de cadenas. Así que simplemente reemplace el tipo de enumeración con un tipo que administre explícitamente la asignación, inicializándola leyendo un archivo o base de datos. Si lo haces bien, obtendrás las propiedades computacionales y la seguridad de tipo de una enumeración. Lo único que pierde es el azúcar sintáctico ... y la estática.

Este enfoque tendrá la ventaja adicional de que puede modificar la asignación de 'descriptores' sin modificar el código fuente de su programa.


Por cierto, la limitación que se está ejecutando es impuesta por el formato de archivo de clase JVM. Un método o constructor tiene un límite de tamaño superior de 2^16 bytes, y un código de inicialización estático de clases se representa como un método especial con un nombre funky.

ACTUALIZACIÓN

Desafortunadamente su solución de auto-respuesta seguirá funcionando en un límite de 64 K diferente ... si se les presiona demasiado. Al dividir el método initialize(), se supera el límite de tamaño del método, pero también existe , que es, un límite de 64 KB en el número de entradas en un grupo constante de clases. Cada literal de cadena requiere una entrada de grupo constante.

+0

estoy corriendo en esto porque quiero hacer exactamente lo contrario - tirando de la información estática en lugar del descriptor en el código. Actualmente, es una tabla de base de datos con aproximadamente 1400 entradas que deseo transferir al código. El motivo es un mayor control sobre los datos y un manejo más sencillo en nuestro gran proyecto. El tema es demasiado complejo para explicarlo brevemente. lectura algún tipo de archivo plano con un conjunto unitario podría ser un camino a seguir. Gracias por esa idea –

+0

@DanielBleisteiner - pero creo que usted estará de acuerdo en que su aplicación no necesita estrictamente un 'enum' * * per se, ya que no utiliza los nombres de enumeración en el código fuente. –

+0

Oh ... resurrección de un hilo muy viejo. Trabajamos con muchas clases diferentes en la forma en que lo describí en mi respuesta a continuación. Nuestros descriptores utilizan acrónimos que son únicos y se asignan a través de operaciones @PostLoad en el modelo. Funcionan de forma similar a los Enums y son accesibles a través de Descriptor.findByAcronym (...) y referencias estáticas finales a singletons en nuestro código. –

5

Esta no es una solución simple, pero puede intentar ... parchear el compilador de Java.

Cuando escribe un enum, el compilador de Java genera una clase que se extiende a java.lang.Enum (posiblemente varias clases, si hay métodos específicos de la constante). La clase tiene algunos campos estáticos (ocultos) que, en el nivel de bytecode, se inicializan con el método especial <clinit>() (que la JVM llama cuando la clase se utiliza por primera vez). Como cualquier otro método, el código para el método <clinit>() está limitado a 65535 bytes. Cada constante contribuye a unos 20 a 22 bytes en el código de bytes <clinit>() (más si hay constructores de constante específica), por lo que llega al límite en aproximadamente 3000 constantes de enumeración.

Ahora el método <clinit>() tiene un nombre gracioso pero no es nada realmente especial; puede invocar otros métodos. El compilador de Java podría dividir el mamut <clinit>() en varios submétodos ocultos que <clinit>() invocarían uno después del otro. El compilador de Java no hace eso actualmente, pero teóricamente podría hacerlo. El resultado sería procesable por cualquier JRE.

O bien, sintetice su clase enum sintéticamente, generando bytecode desde un programa dedicado, posiblemente escrito en Java. En esencia, esto es como escribir tu propio compilador especializado, para un objetivo específico y usar tu propia sintaxis de origen. La biblioteca BCEL puede ayudar.

Tenga en cuenta que existen otros límites que podrían saltar sobre usted. Para cada constante de enumeración, el código estático (el de <clinit>()) utiliza dos "constantes", que son valores internos agregados en la parte "agrupación constante" de la clase compilada. Los dos valores son el nombre de la constante (como una cadena) y la referencia de campo estático resultante. Hay un límite estricto en 65536 entradas constantes de grupo (los índices están en 16 bits), por lo que no más de un poco más de 32000 constantes de enumeración. Un compilador de Java parchado podría caminar alrededor de ese límite al generar varias clases ocultas, cada una con su propio conjunto constante. Un límite más difícil es el número de campos estáticos: cada constante de enumeración se convierte en un campo estático en la clase "enum", y no puede haber más de 65535 campos (estáticos o no) en una clase.

+0

No romperé la compatibilidad de mi bytecode java. Esa no es una opción. –

+2

Pude haber estado poco claro. El "punto" de mi mensaje es que podría hacerse manteniendo el bytecode totalmente compatible con lo que espera la JVM. –

+0

Suena como un proyecto divertido. No romperá la * compatibilidad con bytecode *, pero necesita un * código fuente * diferente, porque la Java simple con javac falla. –

0

Usted puede tratar de anidación interiores clases estáticas dentro de una clase de nivel superior

+0

El problema aquí es el número de anidamientos necesarias para alcanzar el número de elementos que necesito. Tal vez una inicialización de archivo plano de una sola vez durante el tiempo de ejecución sea un camino por recorrer. –

+0

que debería funcionar ... si me permite el atrevimiento, por favor, consideren la actualización a esta pregunta para incluir lo que finalmente fue con – Everyone

+0

He publicado mi solución ... mientras tanto se marque en mañana. –

Cuestiones relacionadas