Можно ли дать переменные экземпляра Enums собственного типа в Groovy?
Я делаю текстовую приключенческую игру в Groovy как своего рода упражнение, и я сталкиваюсь со странной ошибкой.
прямо сейчас у меня есть enum
для направлений, которые игрок сможет пойти, в настоящее время содержащий Север, Юг, Восток, Запад, вверх и вниз.
у меня есть Room
класс, который содержит Map
других комнат и их направления. Когда я добавляю Room
в другой Room
в некоторых Direction
, Я хочу иметь возможность добавить текущую Room
другой Room
на напротив направлении.
пример: если я добавлю соединение из комнаты 1 в комнату 2, идущую на север, я хочу иметь возможность добавить соединение из комнаты 2 в комнату 1, идущую на юг в то же время.
в настоящее время я пытаюсь реализовать это с помощью enum
имени Direction
С вложенной переменной экземпляра opposite
(типа Direction
). Разве это запрещено? Я не получаю ошибки компилятора или что-то еще, но я не могу его получить работать.
вот полный enum
объявления:
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
}
}
и это метод, из которого я его вызываю:
public void addConnection(Direction d, Spot spot){
connections[d] = spot
spot.connections[d.opposite()] = this
}
здесь connections
это public Map<Direction, Spot>
.
в этом случае запись добавляется в connections
это выглядит так:
null:Spot@some_hexadecimal_representation
любая помощь будет большое. Спасибо!
3 ответов
Groovy, похоже, обходит то, что на Java ошибка компиляции:
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
компилятор groovy не жалуется на это, но инициализирует значения перечисления, которые нуждаются в объявлениях вперед как 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.
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
одним из решений, которое, кажется, работает просто отлично в Java, является добавлять static
блок Direction
класс инициализации opposite
значения. Переведенный для Groovy это было бы:
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"
}
что теперь выводит правильные значения:
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
обновление
еще один, возможно, более простое решение может использовать индексы направлений в перечислении, чтобы найти противоположности:
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
}
}
но я нахожу первый более ясным, поскольку ему не нужны эти магические числа для индексов хе-хе.
обновление 2
теперь, я, вероятно, немного в гольф земли здесь, но вы можете получить противоположное направление без необходимости дополнительного поля, просто используя перечисление значений' ordinal()
(индекс):
enum Direction {
North, South, East, West, Up, Down
Direction getOpposite() {
values()[ordinal() + ordinal() % 2 * -2 + 1]
}
}
это не так страшно, как кажется! Даже направления (Север, Восток, вверх) возвращают направление в ordinal() + 1
как их противоположность, в то время как нечетные направления (другие) возвращают те в ordinal() - 1
. Конечно, это сильно зависит от порядка элементов в перечислении, но разве вам не нравится лаконичность? =D
вы можете отложить оценку противоположности, передав ее в закрытие и вызвав закрытие, когда вы хотите противоположное:
public enum Direction {
North({South}), South({North}), East({West}), West({East}), Up({Down}), Down({Up})
private def opp
Direction(opp) {
this.opp = opp
}
public opposite() {
return opp()
}
}
public static void main(String[] args) {
Direction.each { d ->
println "${d} ... ${d.opposite()}"
}
}
выходы:
North ... South South ... North East ... West West ... East Up ... Down Down ... Up
похоже на половину Directions
не инициализируются при вызове конструктора перечисления. То есть, когда вы звоните North(South)
, Юг не был инициализирован. Он следующий в очереди.
вы попали в парадокс курица/яйцо, где все константы перечисления должны быть инициализированы до один из них можно. Похоже, вам нужно будет реорганизовать часть вашего кода, чтобы учесть это. Я мог бы предложить:
public enum Direction {
North(1), South(~1), East(2), West(~2), Up(4), Down(~4);
private int value;
Direction(int d){
value = d;
}
private int getValue() {
return value;
}
public Direction opposite(){
for (Direction d : Direction.values()) {
if (value == ~(d.getValue()))
return d;
}
return null;
}
}
это использует побитовый оператор ~
различать противоположности.