2010-09-29 7 views
11

Ich versuche einen Weg zu finden, Java zu zwingen, einen Aufzählungstyp zu laden/zu initialisieren (der in einer Klasse verschachtelt ist, die eine statische Map enthält).Erzwingen der Initialisierung eines Aufzählungstyps in Java

Dies ist wichtig für mich, weil der Aufzählungstyp einen Konstruktor hat, der die Karte auffüllt, und ohne eine explizite Möglichkeit, diese Aufzählung zu initialisieren, bleibt die Zuordnung leer. Ich habe versucht, Class.forName zu verwenden, aber das scheint nicht zu funktionieren.

Ich denke, ich könnte eine Instanz der Enumeration erstellen (und speichern Sie sie in soem anderen Sammlung oder etwas), aber ich würde gerne wissen, ob es eine elegante Möglichkeit ist, dies zu tun.

+2

Versuchen Wenn Sie nicht die Enum garantiert wird gebaut werden, bevor Sie die Karte verwenden müssen, könnten Sie Ihr Design hier zu überdenken wollen, obwohl Matts Empfehlung eine statische Initialisierer der Verwendung wahrscheinlich erhalten du was du willst. – Bryan

+1

'Class.forName' initialisiert eine Klasse. Wenn es "nicht funktioniert", haben Sie andere Probleme, die Sie nicht erkennen. Warum postest du deinen Code nicht? – irreputable

+0

Grundsätzlich sehe ich eine Ausnahme, die besagt, dass die Klasse "ungültig" ist, und ich weiß sicher, dass ich den richtigen Paketpfad habe. ist Class.forName() * vermutlich * in allen Fällen auf Enums zu arbeiten? –

Antwort

10

Eine Klasse wird geladen, wenn Sie eine Klasse referenzieren. Dies funktioniert für alle Klassen gleich.

Das Problem, das Sie haben, ist wahrscheinlich, dass ein Enum-Wert vor einer statischen Blockierung initialisiert wird. Sie können nicht auf etwas verweisen, das in einem statischen Block in einem Konstruktor initialisiert wird. (Die generelle Initialisierung von statischem Inhalt in einem Konstruktor ist eine BAD-Idee.) Sie müssen die Map im statischen Block und nicht im Konstruktor initialisieren.

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

Dieser sieht wie der beste Ansatz bisher aus. Ich versuche es mal. –

+1

Btw +1 zu trashgod, als er auf ein Beispiel hinwies, das denselben Ansatz verwendet. –

+0

Es hat funktioniert. Und ich stimme Ihrer Behauptung zu, dass "die Initialisierung von statischem Inhalt in einem Konstruktor eine schlechte Idee ist". Ich ging mit diesem Ansatz voran. –

5

Können Sie nicht einfach die Initialisierung der Karte in den statischen Initialisierer des Typs Enum setzen?

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

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

EDIT: Es scheint, dass das Problem war, dass die Enum-Member-Definitionen zuerst kommen müssen. Ich schätze, ich habe das einfach beschönigt. Ich habe das Beispiel korrigiert.

+0

Dies scheint das Problem nicht zu lösen, da alle in SomeEnum erstellten Instanzen sowieso statisch sind. Das Problem bleibt weiterhin: Wie sage ich dem Klassenlader, eine Enumeration zu initialisieren/zu laden? Sobald ich das gelöst habe, sollte mein gesamter statischer Code für das Enum nach Bedarf ausgeführt werden. –

+1

Dann können Sie ein bisschen mehr erarbeiten? Welches Problem verursacht dies? Wie können Sie auf die Karte zugreifen, bevor die Enum initialisiert wird, wenn die Enum die Initialisierung durchführt? Ist die Karte NICHT in der Enum enthalten? –

+0

korrekt. Die Karte ist nicht in der Enumeration enthalten. Und ich kann die Map nicht innerhalb der Enumeration platzieren, da Java es einem Enum-Konstruktor nicht erlaubt, eine statische Auflistung innerhalb einer Enumeration zu füllen. –

3

Sie können einfach etwas in der enum-Klasse referenzieren. Zum Beispiel:

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

Das ist die grundlegende Problemumgehung, die ich verwende. Aber da der Rückgabewert von MyEnum.values ​​() nicht wirklich im Code verwendet wird, bin ich besorgt, dass er vielleicht "optimiert" wird. Andererseits habe ich einen ähnlichen Trick gesehen, der verwendet wurde, um sicherzustellen, dass eine reguläre Klasse initialisiert wird. Vielleicht ist dies ein guter Weg, das Problem zu beheben. –

+1

Ich bin mir ziemlich sicher, dass es genau wegen der Tatsache, dass es möglicherweise eine Klasseninitialisierung verursachen kann, nicht vollständig optimiert werden kann. Java lässt viel weniger "undefiniert" als C oder C++, zum Guten oder zum Schlechten. –

2

Scheint, wie dies genau ist, warum es oft statt direkt Referenzierung Mitglieder zu verwenden Accessormethoden empfohlen. Ihr Problem besteht darin, dass der Code vor der Initialisierung Zugriff auf die Map ermöglicht. Blockieren Sie den willkürlichen Zugriff auf die Map und blenden Sie sie hinter einer Accessor-Methode aus, die dafür sorgt, dass sie initialisiert wird.

+0

+1 für die Unterstützung der Kapselung hier. –