2010-09-29 7 views
11

Próbuję znaleźć sposób wymuszenia na Javie załadowania/zainicjowania typu wyliczeniowego (który jest zagnieżdżony w klasie zawierającej statyczną mapę).Wymuszenie inicjowania wyliczonego typu w Javie

Jest to dla mnie ważne, ponieważ wyliczony typ ma konstruktor, który zapełnia wspomnianą mapę, i bez wyraźnego sposobu inicjowania tego wyliczenia, mapa pozostanie pusta. Próbowałem użyć Class.forName, ale to nie działa.

Przypuszczam, że mógłbym stworzyć instancję enum (i przechowywać ją w innej kolekcji lub innym elemencie), ale chciałbym wiedzieć, czy jest to elegancki sposób na zrobienie tego.

+2

Jeśli nie gwarantuje enum zostanie zbudowana przed musisz korzystać z mapy, warto rozważyć swój projekt tutaj, choć rekomendacja Matta z użyciem statycznego inicjatora będzie prawdopodobnie uzyskać co chcesz. – Bryan

+1

'Class.forName' inicjalizuje klasę. Jeśli "nie działa", kod ma inne problemy, których nie zdajesz sobie z tego sprawy. Dlaczego nie zamieścić swojego kodu. – irreputable

+0

Zasadniczo widzę wyjątek stwierdzający, że klasa jest "nieważna" i wiem na pewno, że mam poprawną ścieżkę pakietu. czy Class.forName() * przypuszczalnie * działa we wszystkich przypadkach na wyliczeniach? –

Odpowiedz

10

Klasa jest ładowana, gdy odwołujesz się do klasy. Działa to tak samo dla wszystkich klas.

Najprawdopodobniej występuje problem polegający na tym, że przed statycznym blokiem zainicjowano wartość wyliczenia. tzn. nie można odwoływać się do czegoś inicjującego w statycznym bloku w konstruktorze. (Generalnie inicjowanie zawartości statycznej w konstruktorze jest ZŁEJ koncepcji) Musisz zainicjalizować Mapę w bloku statycznym, a nie w konstruktorze.

Spróbuj

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

Ten wygląda jak najlepsze podejście do tej pory. Dam ci szansę. –

+1

Btw +1 do trashgod jak wskazał przykład, który używa tego samego podejścia. –

+0

Zadziałało. Zgadzam się z tezą, że "inicjowanie statycznej zawartości w konstruktorze jest ZŁEJ idei". Zastosowałem to podejście. –

5

Nie możesz po prostu wprowadzić inicjalizacji mapy w statycznym inicjalizatorze typu Enum?

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

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

EDYCJA: Wydaje się, że problem polegał na tym, że definicje członków Enum muszą być na pierwszym miejscu. Sądzę, że po prostu to przeszukałem. Naprawiłem przykład.

+0

To nie rozwiązuje problemu, ponieważ wszystkie instancje utworzone w SomeEnum są i tak statyczne. Problem nadal pozostaje: jak mogę poinformować program ładujący klasy, aby zainicjował/załadował wyliczenie? Kiedy to rozwiążę, cały mój statyczny kod dla Enum powinien zostać wykonany w razie potrzeby. –

+1

Następnie możesz rozwinąć nieco więcej? Jaki problem powoduje ten problem? Jak uzyskać dostęp do mapy przed zainicjowaniem Enum, jeśli Enum przeprowadza inicjalizację? Czy mapa NIE jest zawarta w Enum? –

+0

prawidłowe. Mapa nie jest zawarta w wyliczeniu. I nie mogę umieścić mapy w wyliczeniu, ponieważ Java nie zezwala, aby konstruktor wyliczeniowy wypełnił kolekcję statyczną znajdującą się w wyliczeniu. –

3

Możesz po prostu odwołać się do czegoś w klasie wyliczeniowej. Na przykład:

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

To jest podstawowe obejście, z którego korzystam. Ale ponieważ wartość zwracana przez MyEnum.values ​​() nie jest faktycznie użyta w kodzie, obawiam się, że może ona będzie "zoptymalizowana". Z drugiej strony, widziałem podobną sztuczkę używaną do zapewnienia inicjalizacji zwykłej klasy, więc być może jest to dobry sposób na naprawienie problemu. –

+1

Jestem prawie pewien, że nie można go całkowicie zoptymalizować z powodu faktu, że może potencjalnie spowodować inicjalizację klasy. Java pozostawia znacznie mniej "niezdefiniowane" niż C lub C++, na lepsze lub na gorsze. –

2

Wydaje się, że właśnie dlatego często zaleca się stosowanie metod dostępu, zamiast bezpośredniego odwoływania się do członków. Twoim problemem jest to, że kod umożliwia dostęp do mapy przed jej zainicjowaniem. Zablokuj dowolny dostęp do mapy i ukryj go za metodą dostępową, która zapewnia jej zainicjowanie.

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 za zatwierdzenie enkapsulacji tutaj. –

Powiązane problemy