Program Club

잘못된 순방향 참조 및 열거 형

proclub 2020. 12. 15. 19:32
반응형

잘못된 순방향 참조 및 열거 형


타일 ​​그리드로 구성된 자바 게임을 프로그래밍하고 있습니다. 타일의 가장자리를 직관적으로 정의하고 타일이 서로 관련되는 방식을 정의 할 수 있기를 원하지 않습니다. 예를 들어 타일의 반대쪽 가장자리를 가져 오려면을 입력하기 만하면됩니다 TOP.opposite(). 그러나 열거 형을 사용하여 이러한 가장자리를 정의 할 때 생성자에서 적어도 두 개 이상의 참조를 전달해야합니다.

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

간단한 열거 형을 사용하여이 문제를 해결할 수있는 방법이 있습니까?


직관적이지 않은 작업을 수행 할 수 있습니다.

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

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

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

다른 방법이 있습니다.

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

}

그러나 Peter Lawrey의 솔루션은 더 효율적이고 컴파일 시간이 안전합니다.


열거 형 내부에서 정적 내부 클래스를 사용할 수도 있습니다 .

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

여기 에서 도난당했습니다 :)


아래와 비슷한 방법을 정의 할 수 있습니다.

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

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()메서드 등과 같은 것을 만들 때 유용합니다 .


대신 내부 맵을 사용하여 이러한 연관을 정의 할 수 있습니다. 이것은 맵을 초기화 할 때 이미 모든 열거 형 값이 생성 된 경우 작동합니다.

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

내 방법은 서수를 사용하는 것입니다. 이것은 간단한 예이지만 훨씬 더 복잡한 예는 아래를 참조하십시오.

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

나는 이것을 선호했다 :

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

   private Link link;

   private Link getLink() {
     if (link == null) {
        link = Link.valueOf(name());
     }
     return link;
   }

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

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

   private Edge opposite;

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

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

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

참조 URL : https://stackoverflow.com/questions/5678309/illegal-forward-reference-and-enums

반응형