2010-09-29 10 views
11

Estoy intentando encontrar una forma de obligar a Java a cargar/inicializar un tipo enumerado (que está anidado dentro de una clase que contiene un Mapa estático).Forzar inicialización de un tipo enumerado en Java

Esto es importante para mí porque el tipo enumerado tiene un constructor que rellena dicho mapa, y sin una forma explícita de inicializar esta enumeración, el mapa permanecerá vacío. Intenté usar Class.forName, pero parece que no funciona.

Supongo que podría crear una instancia de la enumeración (y almacenarla en otra colección o algo así), pero me gustaría saber si hay una manera elegante de hacerlo.

+2

Si no tiene la garantía de la enumeración se construirá antes de tener que utilizar el mapa, es posible que desee volver a examinar su diseño aquí, aunque la recomendación de utilizar un inicializador estático de Matt posiblemente acceda tu lo que quieres – Bryan

+1

'Class.forName' inicializa una clase. Si "no funciona", tu código tiene otros problemas de los que no te das cuenta. Por qué no publicar tu código – irreputable

+0

Básicamente veo una excepción que dice que la clase es "inválida", y sé con certeza que tengo la ruta correcta del paquete. es Class.forName() * supposed * para trabajar en enumeraciones en todos los casos? –

Respuesta

10

Una clase se carga cuando hace referencia a una clase. Esto funciona igual para todas las clases.

El problema es que es más probable que un valor Enum se inicialice antes de cualquier bloqueo estático. es decir, no puede hacer referencia a algo inicializado en un bloque estático en un constructor. (En general, inicializar el contenido estático en un constructor es una mala idea). Debe inicializar el mapa en el bloque estático, no en el constructor.

Trate

import java.util.Map; 
import java.util.HashMap; 

public enum EnumTest { 
    FOO, BAR, BAZ; 

    private static final Map<String,EnumTest> map = new LinkedHashMap<String,EnumTest>(); 
    static { 
     for(EnumTest e : EnumTest.values()) 
     map.put(e.name(), e); 
    } 

    public static void main(String... args) { 
    System.out.println(EnumTest.map); 
    } 
} 
+0

Este se ve como el mejor enfoque hasta ahora. Lo intentaré. –

+1

Por cierto +1 a trashgod como señaló un ejemplo que utiliza este mismo enfoque. –

+0

Funcionó. Y estoy de acuerdo con su afirmación de que "inicializar el contenido estático en un constructor es una mala idea". Seguí adelante con este enfoque. –

5

¿No puede simplemente colocar la inicialización del mapa en el inicializador estático del tipo Enum?

public enum SomeEnum 
{ 
    Member1, Member2, Member3 ... 

    private static Map<K, SomeEnum> map = ...; 
    static 
    { 
     ...populate map... 
    } 
    ... 

EDITAR: Parece que el problema era que las definiciones de los miembros Enum deben ser lo primero. Supongo que acabo de pasar por alto esto. Arreglé el ejemplo.

+0

Esto no parece resolver el problema ya que todas las instancias creadas dentro de SomeEnum son estáticas de todos modos. El problema aún persiste: ¿cómo le digo al cargador de clases que inicialice/cargue una enumeración? Una vez que resuelva esto, todo mi código estático para Enum debería ejecutarse según sea necesario. –

+1

¿Entonces puedes elaborar un poco más? ¿Qué problema está causando esto? ¿Cómo se puede acceder al mapa antes de inicializar Enum si Enum realiza la inicialización? ¿El mapa NO está dentro del Enum? –

+0

correcto. El mapa no está contenido dentro de la enumeración. Y no puedo ubicar el mapa dentro de la enumeración porque Java no permite que un constructor enum llene una colección estática que está dentro de una enumeración. –

3

Puede simplemente hacer referencia a algo en la clase enum. Por ejemplo:

public class EnumTest { 
    static final Map<String, MyEnum> map = new HashMap<String, MyEnum>(); 

    enum MyEnum { 
    FOO, BAR, BAZ; 

    MyEnum() { 
     map.put(name(), this); 
    } 
    } 
    static { 
    // ensure MyEnum is initialized 
    MyEnum.values(); 
    } 

    public static void main(String[] argsa) { 
    System.out.println(map.size()); 
    } 
} 
+0

Esa es la solución básica que estoy usando. Pero dado que el valor de devolución de MyEnum.values ​​() no se usa realmente en el código, me preocupa que tal vez se "optimice". Por otra parte, he visto un truco similar utilizado para asegurar que se inicie una clase normal, por lo que tal vez esta sea una forma decente de solucionar el problema. –

+1

Estoy bastante seguro de que no se puede optimizar completamente precisamente porque puede causar una inicialización de clase. Java deja mucho menos "indefinido" que C o C++, para bien o para mal. –

2

parece que esto es exactamente por qué a menudo se recomienda el uso de métodos de acceso en lugar de los miembros que hacen referencia directamente. Su problema es que el código permite el acceso al mapa antes de que se inicialice. Bloquee el acceso arbitrario al mapa y escóndelo detrás de un método de acceso que asegure su inicialización.

import java.util.Map; 
import java.util.HashMap; 

public enum EnumTest { 
    FOO, BAR, BAZ; 

    private static Map<String,EnumTest> map = null; 

    public synchronized static Map getMap() { 
    if (map == null) { 
     map = new HashMap<String,EnumTest>(); 
     for (EnumTest e : EnumTest.values()) { 
     map.put(e.name(), e); 
     } 
    } 

    return map; 
    } 

    public static void main(String[] args) { 
    System.out.println(EnumTest.getMap().size()); 
    } 
} 
+0

+1 para endosar la encapsulación aquí. –

Cuestiones relacionadas