Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to give Enums instance variables of their own type in Groovy?

Tags:

java

enums

groovy

I'm making a Text Adventure game in Groovy as a sort of exercise, and I'm running into a strange error.

Right now, I have an enum for the directions that a player will be able to go, currently containing North, South, East, West, Up, and Down.

I have a Room class that holds a Map of other connected rooms and their directions. When I add a Room to another Room at a certain Direction, I want to be able to also add the current Room to the other Room at the opposite direction.

Ex: If I add a connection from Room 1 to Room 2 going north, I want to be able to add a connection from Room 2 to Room 1 going south at the same time.

Currently, I am trying to implement this using an enum named Direction with an attached instance variable opposite (of type Direction). Is this not allowed? I'm not getting compiler errors or anything, but I can't seem to get it to work.

Here's the full enum declaration:

public enum Direction {
    North(South), South(North), East(West), West(East), Up(Down), Down(Up)
    private Direction opposite
    Direction(Direction d){
        opposite = d
    }
    public opposite(){
        return opposite
    }
}

And this is the method that I am calling it from:

public void addConnection(Direction d, Spot spot){
    connections[d] = spot
    spot.connections[d.opposite()] = this
}

where connections is a public Map<Direction, Spot>.

In this case, an entry gets added to connections that looks like this:

null:Spot@some_hexadecimal_representation

Any help would be great. Thank you!

like image 830
Benjamin Kovach Avatar asked Jul 02 '12 22:07

Benjamin Kovach


People also ask

Can enum have instance variables?

Enums are very powerful as they may have instance variables, instance methods, and constructors. Each enum constant should be written in capital letters. Every enum constant is by default internally public static final of type Enum declared.

How do I create an enum instance?

10) You can not create an instance of enums by using a new operator in Java because the constructor of Enum in Java can only be private and Enums constants can only be created inside Enums itself. 11) An instance of Enum in Java is created when any Enum constants are first called or referenced in code.

How do I assign a variable in Groovy?

Variables in Groovy can be defined in two ways − using the native syntax for the data type or the next is by using the def keyword. For variable definitions it is mandatory to either provide a type name explicitly or to use "def" in replacement. This is required by the Groovy parser.

Can enums be changed?

Enum constants are final but it's variable can still be changed. For example, we can use setPriority() method to change the priority of enum constants. We will see it in usage in below example. Since enum constants are final, we can safely compare them using “==” and equals() methods.


1 Answers

Groovy seems to circumvent what in Java is a compilation error:

Main.java:2: illegal forward reference
    North(South), South(North), East(West), West(East), Up(Down), Down(Up);
          ^
Main.java:2: illegal forward reference
    North(South), South(North), East(West), West(East), Up(Down), Down(Up);
                                     ^
Main.java:2: illegal forward reference
    North(South), South(North), East(West), West(East), Up(Down), Down(Up);
                                                           ^
3 errors

The groovy compiler does not complain about that, but initializes the enum values that need forward declarations as null:

public enum Direction {
    North(South), South(North), East(West), West(East), Up(Down), Down(Up)
    Direction(Direction d){
        println "opposite of $this is $d"
    }
}

Direction.South // Force enum instantiation in GroovyConsole.

Outputs:

opposite of North is null
opposite of South is North
opposite of East is null
opposite of West is East
opposite of Up is null
opposite of Down is Up

One solution that seems to work just fine in Java is adding a static block on the Direction class to initialize the opposite values. Translated to Groovy, that would be:

enum Direction {
    North, South, East, West, Up, Down
    private Direction opposite
    Direction getOpposite() { opposite }

    static {
        def opposites = { d1, d2 -> d1.opposite = d2; d2.opposite = d1 }
        opposites(North, South)
        opposites(East, West)
        opposites(Up, Down)
    }
}

Direction.values().each { 
    println "opposite of $it is $it.opposite"
}

Which now prints the correct values:

opposite of North is South
opposite of South is North
opposite of East is West
opposite of West is East
opposite of Up is Down
opposite of Down is Up

Update

Another, maybe more straightforward, solution can using the direction indexes on the enum to find the opposites:

public enum Direction {
    North(1), South(0), East(3), West(2), Up(5), Down(4)
    private oppositeIndex
    Direction getOpposite() { 
        values()[oppositeIndex]
    }
    Direction(oppositeIndex) { 
        this.oppositeIndex = oppositeIndex
    }
}

But i find the first one clearer as it doesn't need those magic numbers for the indexes hehe.

Update 2

Now, i'm probably getting a bit into golfing lands here, but you can get the opposite direction without the need of an extra field, just using the enum values' ordinal() (their index):

enum Direction {
    North, South, East, West, Up, Down
    Direction getOpposite() { 
        values()[ordinal() + ordinal() % 2 * -2 + 1]
    }
}

It's not as scary as it looks! Even directions (North, East, Up) return the direction at ordinal() + 1 as their opposite, while odd directions (the other ones) return the ones at ordinal() - 1. Of course it is heavily relying on the order of the elements in the enum, but, don't you love the succinctness? =D

like image 142
epidemian Avatar answered Oct 08 '22 07:10

epidemian