Java Énumérations: Deux types d'énumérations, chacun contenant des références à l'autre?
Est-il un moyen de contourner la classe de chargement de problèmes causés par le fait d'avoir deux énumérations que font référence les uns aux autres?
J'ai deux séries d'énumérations, Foo et Bar, définis comme suit:
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);
}
}
}
Le code ci-dessus produit en sortie:
A bar Alpha
B bar Delta
C bar Alpha
Alpha foo null
Beta foo null
Delta foo null
Je comprends pourquoi cela arrive - la JVM commence classloading Foo; il voit la Barre.Alpha Foo.Un constructeur, donc il commence à classloading Bar. Il voit le Foo.Une référence dans l'appel à la Barre.Alpha du constructeur, mais (puisque nous sommes encore dans Foo.Un constructeur) Foo.Un est nul à ce point, de sorte Barre.Alpha du constructeur reçoit une valeur null. Si j'inverse les deux boucles for (ou autre Barre de référence avant de Foo), les changements de sortie de sorte que la Barre de valeurs sont tout à fait correct, mais Foo valeurs ne sont pas.
Est-il un moyen de contourner ce problème? Je sais que je peux créer un Mappage statique et une Carte statique dans une 3e classe, mais qui se sent assez hackish pour moi. Je pourrais aussi faire du Foo.getBar() et d'un Bar.getFoo() les méthodes qui font référence à l'extérieur de la carte, de sorte qu'il ne serait même pas changer mon interface (les classes, j'ai utiliser des inspecteurs au lieu de champs publics), mais il se sent en quelque sorte impur pour moi.
(Raison pour laquelle je fais cela dans mon système actuel: Foo et Bar représentent les types de messages que 2 applications envoient l'un à l'autre; le Foo.b et d'un Bar.f champs représentent la réponse attendue pour un type donné de message, donc dans mon exemple de code, lorsque app_1 reçoit un Foo.Un, il doit répondre avec un Bar.Alpha et vice-versa.)
Merci d'avance!
Vous devez vous connecter pour publier un commentaire.
L'un des meilleurs moyens d'y parvenir consisterait à l'aide de la enum polymorphisme technique:
Le code ci-dessus génère la sortie que vous souhaitez:
Voir aussi:
public Bar getBar(boolean nullIfAlpha) { return nullIfAlpha ? null : Bar.Alpha; }
. De toute façon, j'ai édité ma réponse à "l'un des meilleurs" au lieu de cela, car il peut être par opinion. Merci pour votre réponse!Le problème n'est pas tellement "deux énumérations de référence les uns les autres", c'est plus "deux énumérations de référence les uns des autres dans leurs constructeurs". Cette référence circulaire est la partie la plus délicate.
Comment sur l'utilisation de
Foo.setResponse(Bar b)
etBar.setResponse(Foo f)
méthodes? Au lieu de mettre un Foo Bar Foo constructeur (et même un Bar Foo dans la Barre constructeur), vous ne l'initialisation à l'aide d'une méthode? E. g.:Foo:
Bar:
Aussi, vous mentionnez que les Foo et Bar sont les deux types de messages. Serait-il possible de les combiner en un seul type? De ce que je peux voir, leur comportement est le même. Cela ne résout pas la logique circulaire, mais il peut vous donner quelques autres un aperçu de votre design...
Puisqu'il semble que vous allez être de codage en dur, de toute façon, pourquoi ne pas avoir quelque chose comme
pour chaque enum? Il semble que vous avez un certain chevauchement des réponses dans votre exemple, vous pourriez même profiter des cas de tomber à travers.
EDIT:
J'aime Tom à la suggestion de la EnumMap; je pense performance est probablement plus rapide sur le EnumMap, mais le genre de l'élégante construction décrite dans l'efficacité de Java ne semble pas être accordée par ce problème particulier - toutefois, le commutateur solution proposée ci-dessus serait un bon moyen de construire deux statique EnumMaps, la réponse pourrait être quelque chose comme:
EnumMap
si vous préférez que pour unswitch
.Design intéressant. Je vois à votre besoin, mais qu'allez-vous faire lorsque les exigences de décaler légèrement, de sorte que, en réponse à Toto.Epsilon, app_1 doit envoyer soit un Bar.Gamma ou d'un Bar.Machin?
La solution que vous avez examiné et rejeté comme hackish (mettre le lien dans une carte) semble donner beaucoup plus de souplesse, et évite de votre référence circulaire. Il conserve également la responsabilité partitionné: les types de messages eux-mêmes ne devraient pas être tenus de connaître leur réponse, ils doivent?
Vous pouvez utiliser EnumMap, et de le remplir au sein de l'un des enums.
De sortie: