Нелегальная прямая ссылка и перечисления - PullRequest
57 голосов
/ 15 апреля 2011

Я программирую игру на Java, которая состоит из сетки плиток.Я не хочу иметь возможности определять края плиток и то, как они связаны друг с другом, например, чтобы получить противоположный край плитки, я хочу просто набрать TOP.opposite().Тем не менее, при использовании перечислений для определения этих ребер, мне приходится в конечном итоге ссылаться как минимум на два из них в contstructor:

public enum Edge {

   TOP(Edge.BOTTOM), //illegal forward reference
   BOTTOM(Edge.TOP),
   LEFT(Edge.RIGHT), //illegal forward reference
   RIGHT(Edge.LEFT);

   private Edge opposite;

   private Edge(Edge opp){
      this.opposite = opp;
   }

   public Edge opposite(){
      return this.opposite;
   }
}

Есть ли способ обойти эту проблему, используя перечисления, которые так же просто

Ответы [ 11 ]

74 голосов
/ 15 апреля 2011

Вы можете сделать это, что не так интуитивно понятно.

public enum Edge {
    TOP, BOTTOM, LEFT, RIGHT;
    private Edge opposite;

    static {
        TOP.opposite = BOTTOM;
        BOTTOM.opposite = TOP;
        LEFT.opposite = RIGHT;
        RIGHT.opposite = LEFT;
    }
    public Edge opposite(){
        return this.opposite;
    }
}
16 голосов
/ 22 ноября 2013
enum Edge {
    TOP {
        @Override
        public Edge opposite() {
            return BOTTOM;
        }
    },
    BOTTOM {
        @Override
        public Edge opposite() {
            return TOP;
        }
    },
    LEFT {
        @Override
        public Edge opposite() {
            return RIGHT;
        }
    },
    RIGHT {
        @Override
        public Edge opposite() {
            return LEFT;
        }
    };

    public abstract Edge opposite();
}
12 голосов
/ 26 февраля 2015
public enum Edge {

    TOP,
    BOTTOM(Edge.TOP),
    LEFT,
    RIGHT(Edge.LEFT);

    private Edge opposite;

    private Edge() {

    }
    private Edge(Edge opp) {
        this.opposite = opp;
        opp.opposite = this;
    }

    public Edge opposite() {
        return this.opposite;
    }
}
6 голосов
/ 15 апреля 2011

Вот еще один способ

public enum Edge {

    TOP("BOTTOM"),
    BOTTOM("TOP"),
    LEFT("RIGHT"),
    RIGHT("LEFT");

    private String opposite;

    private Edge(String opposite){
        this.opposite = opposite;
    }

    public Edge opposite(){
        return valueOf(opposite);
    }

}

Решение Питера Лоури, однако, более эффективно и безопасно во время компиляции.

5 голосов
/ 22 января 2013

Вы также можете использовать статический внутренний класс внутри перечисления:

public enum EnumTest     
{     
NORTH( Orientation.VERTICAL ),     
SOUTH( Orientation.VERTICAL ),     
EAST( Orientation.HORIZONTAL ),     
WEST( Orientation.HORIZONTAL );     

private static class Orientation  
{  
private static final String VERTICAL = null;     
private static final String HORIZONTAL = null;     
}
}

Украдено у здесь :)

3 голосов
/ 15 апреля 2011

Вы можете просто определить метод, аналогичный приведенному ниже.

public enum Edge {
    TOP,
    BOTTOM,
    LEFT,
    RIGHT;

    public Edge opposite() {
        switch (this) {
            case TOP:
                return Edge.BOTTOM;
            case BOTTOM:
                return Edge.TOP;
            case LEFT:
                return RIGHT;
            case RIGHT:
                return LEFT;
            default:
                throw new RuntimeException("Oh dear");
        }
    }
}
3 голосов
/ 15 апреля 2011

Вы можете создать статический Map, где ключ - это исходное перечисление, а значение - противоположный край. Инициализируйте его в статическом блоке и верните отображение из метода opposite().

private static Map<Edge, Edge> oppostiteMapping;

static {
  oppositeMapping = new EnumMap<Edge, Edge>();
  oppositeMapping.put(TOP, BOTTOM);
  ...
}

public Edge opposite() {
    return oppositeMapping.get(this);
} 

РЕДАКТИРОВАТЬ: , как предложено в комментарии, лучше использовать EnumMap, поэтому я обновил соответственно

Btw. этот подход обычно полезен, когда вы создаете что-то вроде статического fromString() метода и т. д.

1 голос
/ 21 марта 2018

Мой метод с использованием порядкового номера.Это простой пример, но для более сложного примера см. Ниже.

public enum Edge {
    // Don't change the order! This class uses ordinal() in an arithmetic context.
    TOP,    // = 0
    LEFT,   // = 1
    RIGHT,  // = 2
    BOTTOM; // = 3

    public Edge other() {
        return values()[3 - ordinal()];
    }
}

Хотя использование порядкового номера не рекомендуется для хрупкости, использование порядкового номера в том же самом перечислении, как это определено в, является менее хрупким, и этодалее смягчается здесь с комментарием.Хотя приведенный выше пример довольно тривиален, следующий пример - не такой.Сравните исходный путь и способ с использованием порядкового номера:

Из 98 строк:

public enum Axes {
    NONE,
    HORIZONTAL,
    VERTICAL,
    BOTH;

    public Axes add(Axes axes) {
        switch (axes) {
            case HORIZONTAL:
                if (this == NONE)
                    return HORIZONTAL;
                if (this == VERTICAL)
                    return BOTH;
                break;
            case VERTICAL:
                if (this == NONE)
                    return VERTICAL;
                if (this == HORIZONTAL)
                    return BOTH;
                break;
            case BOTH:
                return BOTH;
            default:
                throw new AssertionError(axes);
        }
        return this;
    }

    public Axes remove(Axes axes) {
        switch (axes) {
            case HORIZONTAL:
                if (this == HORIZONTAL)
                    return NONE;
                if (this == BOTH)
                    return VERTICAL;
                break;
            case VERTICAL:
                if (this == VERTICAL)
                    return NONE;
                if (this == BOTH)
                    return HORIZONTAL;
                break;
            case BOTH:
                return NONE;
            default:
                throw new AssertionError(axes);
        }
        return this;
    }

    public Axes toggle(Axes axes) {
        switch (axes) {
            case NONE:
                return this;
            case HORIZONTAL:
                switch (this) {
                    case NONE:
                        return HORIZONTAL;
                    case HORIZONTAL:
                        return NONE;
                    case VERTICAL:
                        return BOTH;
                    case BOTH:
                        return VERTICAL;
                    default:
                        throw new AssertionError(axes);
                }
            case VERTICAL:
                switch (this) {
                    case NONE:
                        return VERTICAL;
                    case HORIZONTAL:
                        return BOTH;
                    case VERTICAL:
                        return NONE;
                    case BOTH:
                        return HORIZONTAL;
                    default:
                        throw new AssertionError(axes);
                }
            case BOTH:
                switch (this) {
                    case NONE:
                        return BOTH;
                    case HORIZONTAL:
                        return VERTICAL;
                    case VERTICAL:
                        return HORIZONTAL;
                    case BOTH:
                        return NONE;
                    default:
                        throw new AssertionError(axes);
                }
            default:
                throw new AssertionError(axes);
        }
    }
}

до 19 строк:

public enum Axes {
    // Don't change the order! This class uses ordinal() as a 2-bit bitmask.
    NONE,       // = 0 = 0b00
    HORIZONTAL, // = 1 = 0b01
    VERTICAL,   // = 2 = 0b10
    BOTH;       // = 3 = 0b11

    public Axes add(Axes axes) {
        return values()[ordinal() | axes.ordinal()];
    }

    public Axes remove(Axes axes) {
        return values()[ordinal() & ~axes.ordinal()];
    }

    public Axes toggle(Axes axes) {
        return values()[ordinal() ^ axes.ordinal()];
    }
}
1 голос
/ 15 апреля 2011

Вместо этого вы можете использовать внутреннюю Карту для определения этих ассоциаций.Это работает, если в момент инициализации карты у вас уже созданы все значения перечисления:

public enum Edge {

  TOP,
  BOTTOM,
  LEFT,
  RIGHT;

  private static final Map<Edge, Edge> opposites = 
        new EnumMap<Edge, Edge>(Edge.class);
  static {
    opposites.put(TOP, BOTTOM);
    opposites.put(BOTTOM, TOP);
    opposites.put(LEFT, RIGHT);
    opposites.put(RIGHT, LEFT);
  }

  public Edge opposite(){
    return opposites.get(this);
  }
}
0 голосов
/ 21 декабря 2016

С Java 8 лямбд:

public enum Edge {
  TOP(() -> Edge.BOTTOM),
  BOTTOM(() -> Edge.TOP),
  LEFT(() -> Edge.RIGHT),
  RIGHT(() -> Edge.LEFT);

  private Supplier<Edge> opposite;

  private Edge(Supplier<Edge> opposite) {
    this.opposite = opposite;
  }

  public Edge opposite() {
    return opposite.get();
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...