[java] 새로운 요소를 추가하기 위해 열거 형을 서브 클래스 화 할 수 있습니까?

기존 열거 형을 가져 와서 다음과 같이 더 많은 요소를 추가하고 싶습니다.

enum A {a,b,c}

enum B extends A {d}

/*B is {a,b,c,d}*/

Java에서 가능합니까?



답변

아니요, Java에서는이 작업을 수행 할 수 없습니다. 다른 것 외에도, d아마도 A( “확장자”라는 일반적인 아이디어가 주어 졌을 것임) 인스턴스 일 것입니다 . 그러나 오직 알고있는 사용자는 그것에 대해 알지 못했을 A것입니다. 가치.

사용 방법에 대해 더 자세히 알려면 대체 솔루션을 제안 할 수 있습니다.


답변

열거 형은 가능한 값의 완전한 열거 형을 나타냅니다. 따라서 (도움이되지 않는) 대답은 ‘아니요’입니다.

실제 문제의 예로는 주중, 주말 및 노조가 있습니다. 요일 내에 모든 요일을 정의 할 수 있지만 주중과 주말에 특별한 속성을 나타낼 수는 없습니다.

우리가 할 수있는 일은 주중 / 주말과 요일 사이에 매핑되는 세 가지 열거 형 유형이 있습니다.

public enum Weekday {
    MON, TUE, WED, THU, FRI;
    public DayOfWeek toDayOfWeek() { ... }
}
public enum WeekendDay {
    SAT, SUN;
    public DayOfWeek toDayOfWeek() { ... }
}
public enum DayOfWeek {
    MON, TUE, WED, THU, FRI, SAT, SUN;
}

또는 요일에 대한 개방형 인터페이스를 사용할 수도 있습니다.

interface Day {
    ...
}
public enum Weekday implements Day {
    MON, TUE, WED, THU, FRI;
}
public enum WeekendDay implements Day {
    SAT, SUN;
}

또는 두 가지 접근 방식을 결합 할 수 있습니다.

interface Day {
    ...
}
public enum Weekday implements Day {
    MON, TUE, WED, THU, FRI;
    public DayOfWeek toDayOfWeek() { ... }
}
public enum WeekendDay implements Day {
    SAT, SUN;
    public DayOfWeek toDayOfWeek() { ... }
}
public enum DayOfWeek {
    MON, TUE, WED, THU, FRI, SAT, SUN;
    public Day toDay() { ... }
}


답변

이에 대한 권장 솔루션은 확장 가능한 열거 형 패턴 입니다.

여기에는 인터페이스를 만들고 현재 열거 형을 사용하는 인터페이스를 사용하는 것이 포함됩니다. 그런 다음 열거 형이 인터페이스를 구현하도록하십시오. 새 열거 형도 인터페이스를 확장하여 상수를 더 추가 할 수 있습니다.


답변

커버 아래에서 ENUM은 컴파일러가 생성 한 일반 클래스입니다. 생성 된 클래스가 확장됩니다 java.lang.Enum. 생성 된 클래스를 확장 할 수없는 기술적 이유는 생성 된 클래스가 final입니다. 최종적인 개념적 이유는이 주제에서 설명합니다. 그러나 토론에 역학을 추가하겠습니다.

다음은 테스트 열거 형입니다.

public enum TEST {
    ONE, TWO, THREE;
}

javap의 결과 코드 :

public final class TEST extends java.lang.Enum<TEST> {
  public static final TEST ONE;
  public static final TEST TWO;
  public static final TEST THREE;
  static {};
  public static TEST[] values();
  public static TEST valueOf(java.lang.String);
}

아마도이 클래스를 직접 입력하고 “최종”을 삭제할 수 있습니다. 그러나 컴파일러는 “java.lang.Enum”을 직접 확장하지 못하게합니다. java.lang.Enum을 확장하지 않기로 결정할 수 있지만 클래스와 파생 클래스는 java.lang.Enum의 인스턴스가 아니므로 실제로는 중요하지 않을 수 있습니다!


답변

enum A {a,b,c}
enum B extends A {d}
/*B is {a,b,c,d}*/

다음과 같이 쓸 수 있습니다 :

public enum All {
    a       (ClassGroup.A,ClassGroup.B),
    b       (ClassGroup.A,ClassGroup.B),
    c       (ClassGroup.A,ClassGroup.B),
    d       (ClassGroup.B)
...
  • ClassGroup.B.getMembers () 는 {a, b, c, d}를 포함합니다

유용한 방법 : 다음과 같은 것을 원한다고 가정 해 봅시다. 이벤트가 있고 열거 형을 사용하고 있습니다. 이러한 열거 형은 유사한 처리로 그룹화 할 수 있습니다. 요소가 많은 작업이있는 경우 일부 이벤트는 작업을 시작하고 일부는 단계적이며 다른 작업은 종료됩니다. 이러한 작업을 수집하고 긴 스위치 케이스를 피하기 위해 예제와 같이 그룹화하고 다음을 사용할 수 있습니다.

if(myEvent.is(State_StatusGroup.START)) makeNewOperationObject()..
if(myEnum.is(State_StatusGroup.STEP)) makeSomeSeriousChanges()..
if(myEnum.is(State_StatusGroup.FINISH)) closeTransactionOrSomething()..

예:

public enum AtmOperationStatus {
STARTED_BY_SERVER       (State_StatusGroup.START),
SUCCESS             (State_StatusGroup.FINISH),
FAIL_TOKEN_TIMEOUT      (State_StatusGroup.FAIL,
                    State_StatusGroup.FINISH),
FAIL_NOT_COMPLETE       (State_StatusGroup.FAIL,
                    State_StatusGroup.STEP),
FAIL_UNKNOWN            (State_StatusGroup.FAIL,
                    State_StatusGroup.FINISH),
(...)

private AtmOperationStatus(StatusGroupInterface ... pList){
    for (StatusGroupInterface group : pList){
        group.addMember(this);
    }
}
public boolean is(StatusGroupInterface with){
    for (AtmOperationStatus eT : with.getMembers()){
        if( eT .equals(this))   return true;
    }
    return false;
}
// Each group must implement this interface
private interface StatusGroupInterface{
    EnumSet<AtmOperationStatus> getMembers();
    void addMember(AtmOperationStatus pE);
}
// DEFINING GROUPS
public enum State_StatusGroup implements StatusGroupInterface{
    START, STEP, FAIL, FINISH;

    private List<AtmOperationStatus> members = new LinkedList<AtmOperationStatus>();

    @Override
    public EnumSet<AtmOperationStatus> getMembers() {
        return EnumSet.copyOf(members);
    }

    @Override
    public void addMember(AtmOperationStatus pE) {
        members.add(pE);
    }
    static { // forcing initiation of dependent enum
        try {
            Class.forName(AtmOperationStatus.class.getName());
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException("Class AtmEventType not found", ex);
        }
    }
}
}
//Some use of upper code:
if (p.getStatus().is(AtmOperationStatus.State_StatusGroup.FINISH)) {
    //do something
}else if (p.getStatus().is(AtmOperationStatus.State_StatusGroup.START)) {
    //do something      
}  

좀 더 고급 추가 :

public enum AtmEventType {

USER_DEPOSIT        (Status_EventsGroup.WITH_STATUS,
              Authorization_EventsGroup.USER_AUTHORIZED,
              ChangedMoneyAccountState_EventsGroup.CHANGED,
              OperationType_EventsGroup.DEPOSIT,
              ApplyTo_EventsGroup.CHANNEL),
SERVICE_DEPOSIT     (Status_EventsGroup.WITH_STATUS,
              Authorization_EventsGroup.TERMINAL_AUTHORIZATION,
              ChangedMoneyAccountState_EventsGroup.CHANGED,
              OperationType_EventsGroup.DEPOSIT,
              ApplyTo_EventsGroup.CHANNEL),
DEVICE_MALFUNCTION  (Status_EventsGroup.WITHOUT_STATUS,
              Authorization_EventsGroup.TERMINAL_AUTHORIZATION,
              ChangedMoneyAccountState_EventsGroup.DID_NOT_CHANGED,
              ApplyTo_EventsGroup.DEVICE),
CONFIGURATION_4_C_CHANGED(Status_EventsGroup.WITHOUT_STATUS,
              ApplyTo_EventsGroup.TERMINAL,
              ChangedMoneyAccountState_EventsGroup.DID_NOT_CHANGED),
(...)

위에서 우리가 실패한 경우 (myEvent.is (State_StatusGroup.FAIL)) 이전 이벤트를 반복하면 다음과 같이 송금을 되돌려 야하는지 쉽게 확인할 수 있습니다.

if(myEvent2.is(ChangedMoneyAccountState_EventsGroup.CHANGED)) rollBack()..

다음에 유용 할 수 있습니다.

  1. 처리 논리에 관한 명시 적 메타 데이터 포함
  2. 다중 상속의 일부 구현
  3. 우리는 클래스 구조를 사용하고 싶지 않습니다. 예. 짧은 상태 메시지 보내기

답변

다음은 열거 형을 다른 열거 형으로 확장하는 방법을 찾은 방법입니다. 매우 성가신 접근 방식입니다.

공통 상수가있는 열거 형이 있다고 가정하십시오.

public interface ICommonInterface {

    String getName();

}


public enum CommonEnum implements ICommonInterface {
    P_EDITABLE("editable"),
    P_ACTIVE("active"),
    P_ID("id");

    private final String name;

    EnumCriteriaComun(String name) {
        name= name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

다음과 같이 수동 확장을 시도 할 수 있습니다.

public enum SubEnum implements ICommonInterface {
    P_EDITABLE(CommonEnum.P_EDITABLE ),
    P_ACTIVE(CommonEnum.P_ACTIVE),
    P_ID(CommonEnum.P_ID),
    P_NEW_CONSTANT("new_constant");

    private final String name;

    EnumCriteriaComun(CommonEnum commonEnum) {
        name= commonEnum.name;
    }

    EnumCriteriaComun(String name) {
        name= name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

물론 상수를 확장해야 할 때마다 SubEnum 파일을 수정해야합니다.


답변

당신이 그것을 놓친 경우, 훌륭한 Joshua Bloch의 저서 ” Java Effective, 2nd edition “에 장이 있습니다.

  • 6 장 열거 형과 주석
    • 항목 34 : 인터페이스를 사용하여 확장 가능한 열거 형 에뮬레이션

여기에서 추출 하십시오 .

그냥 결론 :

확장 가능한 열거 형을 에뮬레이트하기 위해 인터페이스를 사용하는 것의 단점은 구현이 하나의 열거 형에서 다른 열거 형으로 상속 될 수 없다는 것입니다. Operation 예제의 경우 오퍼레이션과 연관된 기호를 저장하고 검색하는 로직이 BasicOperation 및 ExtendedOperation에 복제됩니다. 이 경우 코드가 거의 복제되지 않으므로 중요하지 않습니다. 더 많은 양의 공유 기능이있는 경우이를 도우미 클래스 또는 정적 도우미 메서드에 캡슐화하여 코드 중복을 제거 할 수 있습니다.

요약하면 확장 가능한 열거 형 유형을 작성할 수는 없지만 인터페이스를 구현하는 기본 열거 형 유형에 맞는 인터페이스를 작성하여 에뮬레이션 할 수 있습니다. 이를 통해 클라이언트는 인터페이스를 구현하는 자체 열거 형을 작성할 수 있습니다. 그런 다음 API가 인터페이스 측면에서 작성되었다고 가정하면 기본 열거 형 유형을 사용할 수있는 모든 위치에서이 열거 형을 사용할 수 있습니다.