Java-Enums: Zwei enum-Typen, jeweils mit verweisen auf die jeweils anderen?
Gibt es einen Weg, um die class-loading-Probleme verursacht, indem er zwei Enumerationen, die aufeinander verweisen?
Habe ich zwei Sätze mit Aufzählungen, Foo und Bar definiert wie folgt:
public class EnumTest {
public enum Foo {
A(Bar.Alpha),
B(Bar.Delta),
C(Bar.Alpha);
private Foo(Bar b) {
this.b = b;
}
public final Bar b;
}
public enum Bar {
Alpha(Foo.A),
Beta(Foo.C),
Delta(Foo.C);
private Bar(Foo f) {
this.f = f;
}
public final Foo f;
}
public static void main (String[] args) {
for (Foo f: Foo.values()) {
System.out.println(f + " bar " + f.b);
}
for (Bar b: Bar.values()) {
System.out.println(b + " foo " + b.f);
}
}
}
Den oben genannten code erzeugt als Ausgabe:
A bar Alpha
B bar Delta
C bar Alpha
Alpha foo null
Beta foo null
Delta foo null
Verstehe ich, warum es passiert - die JVM startet zum laden von Klassen Foo; er sieht die Bar.Alpha-Foo.Ein Konstruktor, so beginnt es zum laden von Klassen-Bar. Er sieht die Foo.Ein Verweis im Aufruf Bar.Alpha-Konstruktor, aber (da sind wir immer noch in Foo.Ein Konstruktor) Foo.Eine null an dieser Stelle, also Bar.Alpha-Konstruktor übergeben bekommt eine null. Wenn ich Umgekehrt die beiden Schleifen (oder eine andere Referenz-Bar Foo), der Ausgang sich ändert, so dass die Bar die Werte sind alle richtig, aber Foo Werte nicht.
Gibt es eine Möglichkeit dies zu umgehen? Ich weiß, ich kann eine statische Karte, und eine statische Karte, eine 3. Klasse, aber das fühlt sich ziemlich hackish zu mir. Ich könnte auch Foo.getBar() und Bar.getFoo () - Methoden, die sich auf die externe Karte, also es würde nicht sogar mein interface (die eigentlichen Klassen, die ich habe, verwenden die Inspektoren statt von den öffentlichen Bereichen), aber es fühlt sich immer noch irgendwie unrein zu mir.
(Der Grund, warum ich das hier mache, in meinem aktuellen system: Foo und Bar repräsentieren Arten von Nachrichten, die 2 apps an einander senden; Foo.b und Bar.f Felder repräsentieren die erwartete Reaktion-Typ für eine gegebene Nachricht - also in meinem Beispiel-code, wenn app_1 erhält ein Foo.Eine, die es braucht, um zu Antworten mit einer Bar.Alpha und Umgekehrt.)
Vielen Dank im Voraus!
InformationsquelleAutor Sbodd | 2009-10-01
Du musst angemeldet sein, um einen Kommentar abzugeben.
Einer der besten Wege, mit Hilfe der enum-Polymorphismus-Technik:
Der obige code erzeugt die Ausgabe, die Sie wollen:
Siehe auch:
Ich habe schon gestimmt, bis weiji ' s Ansatz, die sehr interessant ist. Obwohl ich persönlich denke, es ist viel besser, die Art und Weise habe ich empfohlen, weil die zyklische Referenz in seiner enum, einen großen Unterschied zwischen unseren Antworten, mir würde es ebenfalls ermöglichen, Sie zu tun alle runtime-Logik vor der Rückkehr, sobald es erforderlich ist, zum Beispiel:
public Bar getBar(boolean nullIfAlpha) { return nullIfAlpha ? null : Bar.Alpha; }
. Jedenfalls habe ich bearbeitet meine Antwort auf "einer der besten" statt, da es sich nach Meinung basiert. Vielen Dank für Ihre Antwort!InformationsquelleAutor falsarella
Das Problem ist nicht so sehr "zwei Enumerationen, die aufeinander verweisen", es ist mehr "zwei Enumerationen, die sich gegenseitig referenzieren in Ihren Konstruktoren". Dieser Zirkelbezug ist der schwierige Teil.
Wie über die Verwendung
Foo.setResponse(Bar b)
undBar.setResponse(Foo f)
Methoden? Statt Foo Bar Foo-Konstruktor (und auch eine Bar ist Foo in der Bar Konstruktor), müssen Sie die Initialisierung mit einer Methode? E. g.:Foo:
Bar:
Auch, Sie erwähnen, dass Foo und Bar sind zwei Arten von Nachrichten. Wäre es möglich, Sie zu kombinieren in einem einzigen Typ? Von was ich sehen kann, Ihr Verhalten ist hier das gleiche. Dieser fix nicht die zirkuläre Logik, aber es könnte Ihnen einige andere Einblick in dein design...
InformationsquelleAutor weiji
Da es scheint, dass Sie gehen, um hart zu codieren sowieso, warum nicht so etwas wie
für jede enum? Es sieht aus wie Sie einige überlappende Reaktionen in deinem Beispiel, so könnte man auch nutzen Fällen fallen durch.
EDIT:
Mag ich Tom ' s Vorschlag, die EnumMap; ich denke Leistung ist wahrscheinlich schneller auf die EnumMap, aber die Art von elegante Konstruktion beschrieben, in der Effektiven Java scheint nicht zu sein, die sich durch dieses bestimmte problem - aber die switch-Lösung angeboten oben wäre ein guter Weg, um zu konstruieren, die zwei statische EnumMaps, dann ist die Antwort könnte so etwas wie:
EnumMap
wenn Sie es vorziehen, dass zu einemswitch
.(Vorsichtig, wie Sie intialise, obwohl - siehe Effektive Java.)
InformationsquelleAutor Carl
Interessantes design. Ich sehe deine not, aber was werden Sie tun, wenn die Anforderungen shift leicht, so dass in Reaktion auf Foo.Epsilon, app_1 schicken sollte entweder eine Bar.Gamma oder eine Bar.Whatsit?
Die Lösung, die Sie erwogen und verworfen, als hackish (wenn man die Beziehung in eine Karte) zu geben scheint, werden Sie viel mehr Flexibilität, und verhindert, dass die zirkuläre Referenz. Es hält auch die Verantwortung aufgeteilt: die message-Typen selbst sollte nicht dafür verantwortlich sein, zu wissen, Ihre Antwort, sollte Sie?
InformationsquelleAutor CPerkins
Können Sie EnumMap, und füllen Sie es in eine der Enumerationen.
Ausgabe:
InformationsquelleAutor MForm