Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Enums - Logical circular reference [duplicate]

Imagine the following made up example:

public enum Hand {
  ROCK(SCISSORS),
  PAPER(ROCK),
  SCISSORS(PAPER);

  private final Hand beats;

  Hand(Hand beats) {
    this.beats = beats;
  }
}

I will get an error Illegal forward reference for forward referencing SCISSORS.


Is there a way to handle such forward references in Java?

Or how would you model such a situation, where you have a logical circular reference between several enums values?

like image 392
JDC Avatar asked Oct 18 '25 12:10

JDC


1 Answers

You cannot assign SCISSORS to ROCK before it is defined. You can, instead, assign the values in a static block.

I have seen a lot examples where people use String values in the constructors, but this is more concrete to assign the actual values after they have been declared. This is encapsulated and the beats instance variable cannot be changed (unless you use reflection).

public enum Hand {
    ROCK,
    PAPER,
    SCISSORS;

    private Hand beats;

    static {
        ROCK.beats = SCISSORS;
        PAPER.beats = ROCK;
        SCISSORS.beats = PAPER;
    }

    public Hand getBeats() {
        return beats;
    }

    public static void main(String[] args) {
        for (Hand hand : Hand.values()) {
            System.out.printf("%s beats %s%n", hand, hand.getBeats());
        }
    }
}

Output

ROCK beats SCISSORS
PAPER beats ROCK
SCISSORS beats PAPER

Update

Here is an alternate version that uses a map instead. This could be helpful for more complex datatypes.

import java.util.Map;
import java.util.EnumMap;

public class Janken {
    enum Hand {
        ROCK,
        PAPER,
        SCISSORS;
    
        private static final Map<Hand, Hand> BEATS_MAP = Map.of(
            ROCK, SCISSORS,
            PAPER, ROCK,
            SCISSORS, PAPER
        );
    
        public Hand getBeats() {
            return BEATS_MAP.get(this);
        }
    }
    
    public static void main(String[] args) {
        for (Hand hand : Hand.values()) {
            System.out.printf("%s beats %s%n", hand, hand.getBeats());
        }
    }
}

This is a little more verbose, but you could construct the Map as an EnumMap.

private static final Map<Hand, Hand> BEATS_MAP = new EnumMap<>(Hand.class);

static {
    BEATS_MAP.put(ROCK, SCISSORS);
    BEATS_MAP.put(PAPER, ROCK);
    BEATS_MAP.put(SCISSORS, PAPER);
}
like image 88
Mr. Polywhirl Avatar answered Oct 21 '25 02:10

Mr. Polywhirl