可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an enum like the one below, but eclipse says that there are errors in the first definition of each opposite pair.
public enum Baz{ yin(yang), //Cannot reference a field before it is defined yang(yin), good(evil), //Cannot reference a field before it is defined evil(good); public final Baz opposite; Baz(Baz opposite){ this.opposite = opposite; } }
What I want to accomplish is being able to use Baz.something.opposite to get the opposite object of Baz.something. Is there a possible workaround for this? Maybe an empty placeholder for yang and bad before yin and good are defined in this example?
回答1:
You could try something like:
public enum Baz{ yin("yang"), yang("yin"), good("evil"), evil("good"); private String opposite; Baz(String opposite){ this.opposite = opposite; } public Baz getOpposite(){ return Baz.valueOf(opposite); } }
and then reference it as
Baz.something.getOpposite()
That should accomplish what you are looking to do by looking up the enum value by it's string representation. I don't think you can get it to work with the recursive reference to Baz.
回答2:
With the switch statement:
public enum Baz{ yin, yang, good, evil; public Baz getOpposite() { switch (this) { case yin: return yang; case yang: return yin; case good: return evil; case evil: return good; } throw new AssertionError(); }
Or deferred initialization:
public enum Baz{ yin, yang, good, evil; public Baz opposite; static { yin.opposite = yang; yang.opposite = yin; good.opposite = evil; evil.opposite = good; } }
You might wish to make the mutable field private and provide a getter.
回答3:
How about an EnumMap?
public enum Baz { yin, yang, good, evil; private static final Map<Baz, Baz> opposites = new EnumMap<Baz, Baz>(Baz.class); static { opposites.put(yin, yang); opposites.put(yang, yin); opposites.put(good, evil); opposites.put(evil, good); } public Baz getOpposite() { return opposites.get(this); } }
回答4:
Years later, the shortest and most hacky solution
public enum Baz { YIN, // Use uppercase for enum names! YANG, GOOD, EVIL; public Baz opposite() { return values()[ordinal() ^ 1]; } }
It relies on the assumption that each member has an opposite and that they're arranged pairwise. It replaces the field by a method in the hope, that the JVM will optimize the whole overhead away. This is reasonable on desktop, less reasonable on Android.
To eliminate the overhead, I could use the static initializer as many other solutions here.
回答5:
You can also use abstract methods to delay, which has benefits of type safety over the accepted answer.
public enum Baz { yin(new OppositeHolder() { @Override protected Baz getOpposite() { return yang; } }), yang(new OppositeHolder() { @Override protected Baz getOpposite() { return yin; } }), good(new OppositeHolder() { @Override protected Baz getOpposite() { return evil; } }), evil(new OppositeHolder() { @Override protected Baz getOpposite() { return good; } }); private final OppositeHolder oppositeHolder; private Baz(OppositeHolder oppositeHolder) { this.oppositeHolder = oppositeHolder; } protected Baz getOpposite() { return oppositeHolder.getOpposite(); } private abstract static class OppositeHolder { protected abstract Baz getOpposite(); } }
And test code, because I needed it....
import org.junit.Test; import static org.junit.Assert.fail; public class BazTest { @Test public void doTest() { for (Baz baz : Baz.values()) { System.out.println("Baz " + baz + " has opposite: " + baz.getOpposite()); if (baz.getOpposite() == null) { fail("Opposite is null"); } } } }
回答6:
And yet another possible implementation (similar to some of the other solutions, but with a HashMap).
import java.util.Map; import java.util.HashMap; public enum Baz { yin, yang, good, evil; private static Map<Baz, Baz> opposites = new HashMap<Baz, Baz>(); static { opposites.put(yin, yang); opposites.put(yang, yin); opposites.put(good, evil); opposites.put(evil, good); } public Baz getOpposite() { return opposites.get(this); } }
回答7:
One more alternative :) using a map. It's quite verbose, but this way you can define each pair only once, the other direction is inferred.
enum Baz { YIN, YANG, GOOD, EVIL; private static final Map<Baz, Baz> opposites = new EnumMap<>(Baz.class); static { opposites.put(YIN, YANG); opposites.put(GOOD, EVIL); for (Entry<Baz, Baz> entry : opposites.entrySet()) { opposites.put(entry.getValue(), entry.getKey()); } } public Baz opposite() { return opposites.get(this); } }
Personally, I like meriton's second example the best.
回答8:
And then there is the totally OTT solution.
public enum Baz { yin, yang, good, evil, right, wrong, black, white; private static class AutoReversingMap<K extends Enum<K>> extends EnumMap<K, K> { public AutoReversingMap(Class<K> keys) { super(keys); } // Make put do both the forward and the reverse. public K put(K key, K value) { super.put(key, value); super.put(value, key); // Better to return null here than a misleading real return of one of the supers. return null; } } private static final Map<Baz, Baz> opposites = new AutoReversingMap<Baz>(Baz.class); static { // Assume even and odd ones are opposites. for (int i = 0; i < Baz.values().length; i += 2) { opposites.put(Baz.values()[i], Baz.values()[i + 1]); } } public Baz getOpposite() { return opposites.get(this); } }