[ACCEPTED]-Java Enums: Two enum types, each containing references to each other?-cyclic-reference
One of the best ways would be using the 2 enum polymorphism technique:
public class EnumTest {
public enum Foo {
A {
@Override
public Bar getBar() {
return Bar.Alpha;
}
},
B {
@Override
public Bar getBar() {
return Bar.Delta;
}
},
C {
@Override
public Bar getBar() {
return Bar.Alpha;
}
},
;
public abstract Bar getBar();
}
public enum Bar {
Alpha {
@Override
public Foo getFoo() {
return Foo.A;
}
},
Beta {
@Override
public Foo getFoo() {
return Foo.C;
}
},
Delta {
@Override
public Foo getFoo() {
return Foo.C;
}
},
;
public abstract Foo getFoo();
}
public static void main(String[] args) {
for (Foo f : Foo.values()) {
System.out.println(f + " bar " + f.getBar());
}
for (Bar b : Bar.values()) {
System.out.println(b + " foo " + b.getFoo());
}
}
}
The above code produces the output you 1 want:
A bar Alpha
B bar Delta
C bar Alpha
Alpha foo A
Beta foo C
Delta foo C
See also:
The issue isn't so much "two enums reference 12 each other", it's more "two enums reference 11 each other in their constructors". This circular reference is 10 the tricky part.
How about using Foo.setResponse(Bar b)
and Bar.setResponse(Foo f)
methods? Instead 9 of setting a Foo's Bar in the Foo constructor 8 (and similarly a Bar's Foo in the Bar constructor), you 7 do the initialization using a method? E.g.:
Foo:
public enum Foo {
A, B, C;
private void setResponse(Bar b) {
this.b = b;
}
private Bar b;
public Bar getB() {
return b;
}
static {
A.setResponse(Bar.Alpha);
B.setResponse(Bar.Delta);
C.setResponse(Bar.Alpha);
}
}
Bar:
public enum Bar {
Alpha, Beta, Delta;
private void setResponse(Foo f) {
this.f = f;
}
private Foo f;
public Foo getF() {
return f;
}
static {
Alpha.setResponse(Foo.A);
Beta.setResponse(Foo.C);
Delta.setResponse(Foo.C);
}
}
Also, you 6 mention that Foo and Bar are two types of 5 messages. Would it be possible to combine 4 them into a single type? From what I can 3 see, their behavior here is the same. This 2 doesn't fix the circular logic, but it might 1 give you some other insight into your design...
Since it seems you're going to be hard-coding 13 anyways, why not have something like
public static Bar responseBar(Foo f) {
switch(f) {
case A: return Bar.Alpha;
// ... etc
}
}
for 12 each enum? It looks like you have some 11 overlapping responses in your example, so 10 you could even take advantage of cases falling 9 through.
EDIT:
I like Tom's suggestion of 8 the EnumMap; I think performance is probably 7 faster on the EnumMap, but the sort of elegant 6 construction described in Effective Java 5 doesn't seem to be afforded by this particular 4 problem - however, the switch solution offered 3 above would be a good way to construct two 2 static EnumMaps, then the response could 1 be something like:
public static Bar response(Foo f) { return FooToBar.get(f); }
public static Foo response(Bar b) { return BarToFoo.get(b); }
Interesting design. I see your need, but 10 what are you going to do when the requirements 9 shift slightly, so that in response to Foo.Epsilon, app_1 8 should send either a Bar.Gamma or a Bar.Whatsit?
The 7 solution you considered and discarded as 6 hackish (putting the relation into a map) seems 5 to give you much more flexibility, and avoids 4 your circular reference. It also keeps 3 the responsibility partitioned: the message 2 types themselves shouldn't be responsible 1 for knowing their response, should they?
You can use EnumMap, and fill it within 1 one of the enums.
private static EnumMap<Foo, LinkedList<Bar>> enumAMap;
public static void main(String[] args) throws Exception {
enumAMap = new EnumMap<Foo, LinkedList<Bar>>(Foo.class);
System.out.println(Bar.values().length); // initialize enums, prevents NPE
for (Foo a : Foo.values()) {
for (Bar b : enumAMap.get(a)) {
System.out.println(a + " -> " + b);
}
}
}
public enum Foo {
Foo1(1),
Foo2(2);
private int num;
private Foo(int num) {
this.num = num;
}
public int getNum() {
return num;
}
}
public enum Bar {
Bar1(1, Foo.Foo1),
Bar2(2, Foo.Foo1),
Bar3(3, Foo.Foo2),
Bar4(4, Foo.Foo2);
private int num;
private Foo foo;
private Bar(int num, Foo foo) {
this.num = num;
this.foo = foo;
if (!enumAMap.containsKey(foo)) {
enumAMap.put(foo, new LinkedList<Bar>());
}
enumAMap.get(foo).addLast(this);
}
public int getNum() {
return num;
}
public Foo getFoo() {
return foo;
}
}
Output:
4
Foo1 -> Bar1
Foo1 -> Bar2
Foo2 -> Bar3
Foo2 -> Bar4
Here is an updated version of @falsarella's 3 answer. This uses Java 8 Lambdas and java.util.function, however 2 the principal is the same. It's just a 1 little more succinct with the newer syntax:
import java.util.function.Supplier;
public class EnumTest {
public enum Foo {
A(() -> Bar.Alpha), B(() -> Bar.Beta), C(() -> Bar.Delta);
private final Supplier<Bar> bar;
private Foo(Supplier<Bar> bar) {
this.bar = bar;
}
public Bar getBar() {
return bar.get();
}
}
public enum Bar {
Alpha(() -> Foo.A), Beta(() -> Foo.B), Delta(() -> Foo.C);
private final Supplier<Foo> foo;
private Bar(Supplier<Foo> foo) {
this.foo = foo;
}
public Foo getFoo() {
return foo.get();
}
}
public static void main(String[] args) {
for (Foo f : Foo.values()) {
System.out.println(f + " bar " + f.getBar());
}
for (Bar b : Bar.values()) {
System.out.println(b + " foo " + b.getFoo());
}
}
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.