13

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?

8 Answers 8

14

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I had not heard of deferred initialization, this seems like a good solution, but jcern's String version seems like the best method.
Plus 1 for static initialization
13

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 Comments

+1 You're right, you can't get it to work using the actual objects because evil won't be defined until after good is already initialized, but switching them doesn't work because then good isn't defined when evil is initialized. Good idea to use the names :)
Replacing typed enum values with untyped strings is not a good solution. The whole point of enums is to gave type safety to constants, and strings do not give you that. Plus, there is a much better solutions - doing this in a static initializer block, as @meriton points out in his answer.
4

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); } } 

Comments

3

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 Comments

This answer is incorrect: The obfuscation with the anonymous class does not delay reading the static field, specifically, Baz.yin.oppositeHolder.opposite is null. All this does is confuse the compiler enough that it no longer warns about you reading a static final field before it is assigned.
Quite true, I meant to edit this but forgot about it. Updated now with a working version.
... and how would you ensure that setOpposite is invoked before somebody reads the opposite?
Updated. There are many ways to achieve this effect. It is frowned on to call an instance method within a constructor, but I've left this way for brevity.
... and got it wrong: The static field is read before it is assigned, in particular, Baz.yin.getOpposite() returns null ...
|
1

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.

Comments

0

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); } } 

3 Comments

Did you just copy&paste OldCurmudgeon's answer? 0.o
Well, he used a HashMap instead of EnumMap, that counts! :D But yeah, the map pattern is pretty straightforward.
No he didn't. We posted at the same time. Note that I use an EnumMap instead of a HashMap. Spooky though eh?
0

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); } } 

2 Comments

Yeah, over-the-top may be right... This is a great and thorough answer, but I think simpler answer may be better in this case. (also, I may add a value that is its own opposite, in which case, this isn't ideal)
I totally agree. This was more for a bit of fun. I think @meriton's second suggestion is the neatest.
0

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.

4 Comments

It isn't years later, only a few months. This is a good answer and I like its simplicity. I prefer lowercase on Java enum instances because they are practically the same as static final properties. Would the JVM really optimize out opposite? I'm not sure how much the JVM optimizes out.
@user1494396 Simple short method call get always inlined whenever the JVM thinks it's performance-relevant. The call to values() normally includes cloning, but this is the easiest case for escape analysis. So I'd hope that nothing but field load, xor and static field load remain. This is still 3x as much work as with a field, but I guess that finding a real world example, where it can be measured is hard.
@user1494396 What else should be written in uppercase if not enums? The coding conventions says that constants should be, but it doesn't explain what a constant is. Maybe a compile-time constant, maybe a final (static?) variable pointing to an immutable object?
Honestly, static variables and enum values should be always in uppercase.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.