sourcecode

새로운 요소를 추가하기 위해 Enum을 하위 분류할 수 있습니까?

copyscript 2022. 7. 30. 18:18
반응형

새로운 요소를 추가하기 위해 Enum을 하위 분류할 수 있습니까?

기존 열거형을 가져와 다음과 같이 요소를 추가합니다.

enum A {a,b,c}

enum B extends A {d}

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

자바에서도 가능합니까?

아니요, Java에서는 할 수 없습니다. 것 말고도d그렇다면 아마도 의 예가 될 것이다.A으로는 '사상이지만, '사상'밖에, '사상'은 '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상', '사상',A이 경우 열거형이 잘 알려진 값 집합이라는 의미가 없어집니다.

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

enums는 가능한 값의 완전한 열거를 나타냅니다.따라서 (도움이 되지 않는) 대답은 '아니오'입니다.

실제 문제의 예로는 평일, 주말, 그리고 조합, 요일을 들 수 있습니다.요일 내에서는 모든 요일을 정의할 수 있지만 주중과 주말 모두 특별한 속성을 나타낼 수 없습니다.

평일/주말일과 요일 간의 매핑이 있는 세 가지 열거형을 사용할 수 있습니다.

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

또는, 다음의 2개의 어프로치를 조합할 수도 있습니다.

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 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}이(가) 포함되어 있습니다.

유용성: 예를 들어 다음과 같은 것이 필요합니다.우리는 이벤트가 있고 에넘을 사용하고 있다.이러한 enum은 유사한 처리를 통해 그룹화할 수 있습니다.많은 요소를 사용하여 작업을 수행할 경우 일부 이벤트가 작동을 시작하고, 일부는 단순한 단계이고, 다른 하나는 작동을 종료합니다.이러한 조작을 수집해, 긴 스위치 케이스를 회피하기 위해서, 예시와 같이 그것들을 그룹화해 사용할 수 있습니다.

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_Status Group)FAIL)를 통해 이전 이벤트를 반복하여 다음 방법으로 송금액을 반환해야 하는지 여부를 쉽게 확인할 수 있습니다.

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

다음과 같은 경우에 도움이 됩니다.

  1. 프로세싱 로직에 대한 설명 메타 데이터 포함, 기억해야 할 사항 감소
  2. 몇 가지 멀티 퍼포먼스 구현
  3. 짧은 상태 메시지를 보내는 등의 클래스 구조를 사용하지 않습니다.

혹시 모르시겠지만, 훌륭한 조슈아 블로흐의 책 "효과적인 자바, 2nd 에디션"에 한 장이 있습니다.

  • 6장 - 개요 및 주석
  • 항목 34: 인터페이스를 사용한 확장 가능한 Enum 에뮬레이트

결론은 다음과 같습니다.

확장 가능한 enum을 에뮬레이트하기 위해 인터페이스를 사용하는 것의 작은 단점은 이러한 구현은 어떤 열거형에서 다른 열거형으로 상속될 수 없다는 것입니다.이 조작의 예에서는 조작과 관련된 기호를 저장하고 취득하는 논리는 Basic Operation과 Extended Operation에 중복되어 있습니다.이 경우 중복되는 코드가 거의 없기 때문에 문제가 되지 않습니다.더 많은 양의 공유 기능이 있는 경우 도우미 클래스 또는 정적 도우미 방식으로 캡슐화하여 코드 중복을 제거할 수 있습니다.

요약하면 확장 가능한 열거형은 쓸 수 없지만 인터페이스를 구현하는 기본 열거형을 사용하도록 인터페이스를 작성하여 에뮬레이트할 수 있습니다.이것에 의해, 클라이언트는 인터페이스를 실장하는 독자적인 에넘을 쓸 수 있습니다.이러한 enum은 API가 인터페이스에 관해 기술되어 있다고 가정할 때 기본 열거형을 사용할 수 있는 모든 곳에서 사용할 수 있습니다.

다음은 열거형을 다른 열거형으로 확장하는 방법을 찾아낸 방법입니다.이 방법은 매우 스트레이트 포워드 접근법입니다.

공통 상수가 있는 열거형이 있다고 가정합니다.

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 파일을 수정해야 합니다.

@Tom Hawtin - tackline 답변을 기반으로 스위치지원을 추가합니다.

interface Day<T> {
    ...
  T valueOf();
}

public enum Weekday implements Day<Weekday> {
    MON, TUE, WED, THU, FRI;
   Weekday valueOf(){
     return valueOf(name());
   }
}

public enum WeekendDay implements Day<WeekendDay> {
    SAT, SUN;
   WeekendDay valueOf(){
     return valueOf(name());
   }
}

Day<Weekday> wds = Weekday.MON;
Day<WeekendDay> wends = WeekendDay.SUN;

switch(wds.valueOf()){
    case MON:
    case TUE:
    case WED:
    case THU:
    case FRI:
}

switch(wends.valueOf()){
    case SAT:
    case SUN:
}

나는 enums가 확장성이 없기 때문에 피하는 경향이 있습니다.A가 라이브러리에 있고 B가 자체 코드에 있는 경우 OP의 예를 따르려면 열거형인 경우 A를 확장할 수 없습니다.enum을 다음과 같이 치환할 수 있습니다.

// access like enum: A.a
public class A {
    public static final A a = new A();
    public static final A b = new A();
    public static final A c = new A();
/*
 * In case you need to identify your constant
 * in different JVMs, you need an id. This is the case if
 * your object is transfered between
 * different JVM instances (eg. save/load, or network).
 * Also, switch statements don't work with
 * Objects, but work with int.
 */
    public static int maxId=0;
    public int id = maxId++;
    public int getId() { return id; }
}

public class B extends A {
/*
 * good: you can do like
 * A x = getYourEnumFromSomeWhere();
 * if(x instanceof B) ...;
 * to identify which enum x
 * is of.
 */
    public static final A d = new A();
}

public class C extends A {
/* Good: e.getId() != d.getId()
 * Bad: in different JVMs, C and B
 * might be initialized in different order,
 * resulting in different IDs.
 * Workaround: use a fixed int, or hash code.
 */
    public static final A e = new A();
    public int getId() { return -32489132; };
}

피해야 할 구덩이가 몇 개 있으니 코드에 있는 코멘트를 보세요.필요에 따라 Enum을 대체할 수 있는 견고하고 확장 가능한 옵션입니다.

이렇게 해서 정적 이니셜라이저에서 런타임 체크를 통해 열거 상속 패턴을 향상시킵니다.BaseKind#checkEnumExtender"syslog" 열거형이 기본 열거형의 모든 값을 정확히 동일한 방법으로 선언하는 것을 확인합니다.#name() ★★★★★★★★★★★★★★★★★」#ordinal()이치노

값을 선언하기 위한 복사 붙여넣기 작업은 계속 진행되지만, 누군가가 값을 확장하지 않고 기본 클래스에서 값을 추가하거나 수정하면 프로그램이 빠르게 실패합니다.

서로 확장되는 서로 다른 Enum에 대한 일반적인 동작:

public interface Kind {
  /**
   * Let's say we want some additional member.
   */
  String description() ;

  /**
   * Standard {@code Enum} method.
   */
  String name() ;

  /**
   * Standard {@code Enum} method.
   */
  int ordinal() ;
}

확인 메서드가 있는 기본 열거형:

public enum BaseKind implements Kind {

  FIRST( "First" ),
  SECOND( "Second" ),

  ;

  private final String description ;

  public String description() {
    return description ;
  }

  private BaseKind( final String description ) {
    this.description = description ;
  }

  public static void checkEnumExtender(
      final Kind[] baseValues,
      final Kind[] extendingValues
  ) {
    if( extendingValues.length < baseValues.length ) {
      throw new IncorrectExtensionError( "Only " + extendingValues.length + " values against "
          + baseValues.length + " base values" ) ;
    }
    for( int i = 0 ; i < baseValues.length ; i ++ ) {
      final Kind baseValue = baseValues[ i ] ;
      final Kind extendingValue = extendingValues[ i ] ;
      if( baseValue.ordinal() != extendingValue.ordinal() ) {
        throw new IncorrectExtensionError( "Base ordinal " + baseValue.ordinal()
            + " doesn't match with " + extendingValue.ordinal() ) ;
      }
      if( ! baseValue.name().equals( extendingValue.name() ) ) {
        throw new IncorrectExtensionError( "Base name[ " + i + "] " + baseValue.name()
            + " doesn't match with " + extendingValue.name() ) ;
      }
      if( ! baseValue.description().equals( extendingValue.description() ) ) {
        throw new IncorrectExtensionError( "Description[ " + i + "] " + baseValue.description()
            + " doesn't match with " + extendingValue.description() ) ;
      }
    }
  }


  public static class IncorrectExtensionError extends Error {
    public IncorrectExtensionError( final String s ) {
      super( s ) ;
    }
  }

}

확장 샘플:

public enum ExtendingKind implements Kind {
  FIRST( BaseKind.FIRST ),
  SECOND( BaseKind.SECOND ),
  THIRD( "Third" ),
  ;

  private final String description ;

  public String description() {
    return description ;
  }

  ExtendingKind( final BaseKind baseKind ) {
    this.description = baseKind.description() ;
  }

  ExtendingKind( final String description ) {
    this.description = description ;
  }

}

다른 방법으로 접근해 보시길 권합니다.

기존 열거를 확장하는 대신 더 큰 열거를 만들고 그 하위 집합을 만듭니다.예를 들어 PET라고 하는 열거형이 있고, 그것을 애니멀로 확장하고 싶은 경우는, 다음의 조작을 실시해 주세요.

public enum ANIMAL {
    WOLF,CAT, DOG
} 
EnumSet<ANIMAL> pets = EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);

애완동물은 불변의 컬렉션이 아니므로 안전을 위해 Guava 또는 Java9을 사용하는 것이 좋습니다.

Enum을 확장하는 것이 언어 구현 수준에서 적절하지 않은 이유를 이해하기 위해 확장 Enum 인스턴스를 기본 Enum만 인식하는 루틴으로 전달하면 어떻게 되는지 고려합니다.컴파일러가 모든 케이스를 커버한다고 약속한 스위치는 실제로 확장 Enum 값을 커버하지 않습니다.

이는 Java Enum 값이 C와 같은 정수가 아님을 더욱 강조합니다.예를 들어 배열 인덱스로 Java Enum을 사용하려면 명시적으로 명령() 멤버를 요청해야 합니다.Java Enum에 임의의 정수 값을 지정하려면 명시적으로 필드를 추가하고 그 이름 있는 멤버를 참조해야 합니다.

이것은 OP의 희망에 대한 코멘트가 아닙니다.Java가 왜 그렇게 하지 않는지에 대한 코멘트입니다.

저도 같은 문제를 겪었기 때문에 제 견해를 게재하고 싶습니다.다음과 같은 작업을 수행하는 데는 몇 가지 동기 부여 요인이 있다고 생각합니다.

  • 일부 관련 열거형 코드를 가지지만 클래스는 다릅니다.이 경우 연관된 열거형에 정의된 여러 코드를 포함하는 기본 클래스가 있습니다.나중에 (오늘!)기본 클래스에 몇 가지 새로운 기능을 제공하려고 했습니다. 즉, 열거형의 새 코드를 의미하기도 했습니다.
  • 파생 클래스는 기본 클래스의 열거형뿐만 아니라 기본 클래스의 열거형도 지원합니다.중복된 열거값이 없습니다!따라서 상위 항목의 열거와 새 값을 포함하는 하위 클래스의 열거를 갖는 방법.

인터페이스를 사용한다고 해서 실제로 절단되는 것은 아닙니다.우연히 중복된 열거값을 얻을 수 있습니다.바람직하지 않다.

enums를 조합하는 것만으로 끝납니다.이것에 의해, 관련하는 클래스에 보다 밀접하게 관련지어져 있지 않아도, 중복하는 값이 없어집니다.하지만, 나는 중복된 문제가 나의 주된 관심사라고 생각했다.

동료의 이 우아한 솔루션이 이 긴 투고에서도 보여지기를 바라며, 인터페이스 접근법 및 그 이후를 따르는 서브클래싱을 위한 접근방식을 공유하고자 합니다.

여기에서는 커스텀 예외를 사용하고 있으며, 이 코드는 예외로 대체하지 않는 한 컴파일되지 않습니다.

이 문서는 광범위하기 때문에 여러분 대부분이 이해할 수 있기를 바랍니다.

모든 서브클래스 열거형에서 구현해야 하는 인터페이스.

public interface Parameter {
  /**
   * Retrieve the parameters name.
   *
   * @return the name of the parameter
   */
  String getName();

  /**
   * Retrieve the parameters type.
   *
   * @return the {@link Class} according to the type of the parameter
   */
  Class<?> getType();

  /**
   * Matches the given string with this parameters value pattern (if applicable). This helps to find
   * out if the given string is a syntactically valid candidate for this parameters value.
   *
   * @param valueStr <i>optional</i> - the string to check for
   * @return <code>true</code> in case this parameter has no pattern defined or the given string
   *         matches the defined one, <code>false</code> in case <code>valueStr</code> is
   *         <code>null</code> or an existing pattern is not matched
   */
  boolean match(final String valueStr);

  /**
   * This method works as {@link #match(String)} but throws an exception if not matched.
   *
   * @param valueStr <i>optional</i> - the string to check for
   * @throws ArgumentException with code
   *           <dl>
   *           <dt>PARAM_MISSED</dt>
   *           <dd>if <code>valueStr</code> is <code>null</code></dd>
   *           <dt>PARAM_BAD</dt>
   *           <dd>if pattern is not matched</dd>
   *           </dl>
   */
  void matchEx(final String valueStr) throws ArgumentException;

  /**
   * Parses a value for this parameter from the given string. This method honors the parameters data
   * type and potentially other criteria defining a valid value (e.g. a pattern).
   *
   * @param valueStr <i>optional</i> - the string to parse the parameter value from
   * @return the parameter value according to the parameters type (see {@link #getType()}) or
   *         <code>null</code> in case <code>valueStr</code> was <code>null</code>.
   * @throws ArgumentException in case <code>valueStr</code> is not parsable as a value for this
   *           parameter.
   */
  Object parse(final String valueStr) throws ArgumentException;

  /**
   * Converts the given value to its external form as it is accepted by {@link #parse(String)}. For
   * most (ordinary) parameters this is simply a call to {@link String#valueOf(Object)}. In case the
   * parameter types {@link Object#toString()} method does not return the external form (e.g. for
   * enumerations), this method has to be implemented accordingly.
   *
   * @param value <i>mandatory</i> - the parameters value
   * @return the external form of the parameters value, never <code>null</code>
   * @throws InternalServiceException in case the given <code>value</code> does not match
   *           {@link #getType()}
   */
  String toString(final Object value) throws InternalServiceException;
}

구현 중인 ENUM 기본 클래스입니다.

public enum Parameters implements Parameter {
  /**
   * ANY ENUM VALUE
   */
  VALUE(new ParameterImpl<String>("VALUE", String.class, "[A-Za-z]{3,10}"));

  /**
   * The parameter wrapped by this enum constant.
   */
  private Parameter param;

  /**
   * Constructor.
   *
   * @param param <i>mandatory</i> - the value for {@link #param}
   */
  private Parameters(final Parameter param) {
    this.param = param;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getName() {
    return this.param.getName();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<?> getType() {
    return this.param.getType();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean match(final String valueStr) {
    return this.param.match(valueStr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void matchEx(final String valueStr) {
    this.param.matchEx(valueStr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object parse(final String valueStr) throws ArgumentException {
    return this.param.parse(valueStr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString(final Object value) throws InternalServiceException {
    return this.param.toString(value);
  }
}

기본 클래스에서 "이어받는" 하위 클래스 ENUM입니다.

public enum ExtendedParameters implements Parameter {
  /**
   * ANY ENUM VALUE
   */
  VALUE(my.package.name.VALUE);

  /**
   * EXTENDED ENUM VALUE
   */
  EXTENDED_VALUE(new ParameterImpl<String>("EXTENDED_VALUE", String.class, "[0-9A-Za-z_.-]{1,20}"));

  /**
   * The parameter wrapped by this enum constant.
   */
  private Parameter param;

  /**
   * Constructor.
   *
   * @param param <i>mandatory</i> - the value for {@link #param}
   */
  private Parameters(final Parameter param) {
    this.param = param;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getName() {
    return this.param.getName();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<?> getType() {
    return this.param.getType();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean match(final String valueStr) {
    return this.param.match(valueStr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void matchEx(final String valueStr) {
    this.param.matchEx(valueStr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object parse(final String valueStr) throws ArgumentException {
    return this.param.parse(valueStr);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString(final Object value) throws InternalServiceException {
    return this.param.toString(value);
  }
}

마지막으로 범용 파라미터 Impl을 사용하여 유틸리티를 추가합니다.

public class ParameterImpl<T> implements Parameter {
  /**
   * The default pattern for numeric (integer, long) parameters.
   */
  private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+");

  /**
   * The default pattern for parameters of type boolean.
   */
  private static final Pattern BOOLEAN_PATTERN = Pattern.compile("0|1|true|false");

  /**
   * The name of the parameter, never <code>null</code>.
   */
  private final String name;

  /**
   * The data type of the parameter.
   */
  private final Class<T> type;

  /**
   * The validation pattern for the parameters values. This may be <code>null</code>.
   */
  private final Pattern validator;

  /**
   * Shortcut constructor without <code>validatorPattern</code>.
   *
   * @param name <i>mandatory</i> - the value for {@link #name}
   * @param type <i>mandatory</i> - the value for {@link #type}
   */
  public ParameterImpl(final String name, final Class<T> type) {
    this(name, type, null);
  }

  /**
   * Constructor.
   *
   * @param name <i>mandatory</i> - the value for {@link #name}
   * @param type <i>mandatory</i> - the value for {@link #type}
   * @param validatorPattern - <i>optional</i> - the pattern for {@link #validator}
   *          <dl>
   *          <dt style="margin-top:0.25cm;"><i>Note:</i>
   *          <dd>The default validation patterns {@link #NUMBER_PATTERN} or
   *          {@link #BOOLEAN_PATTERN} are applied accordingly.
   *          </dl>
   */
  public ParameterImpl(final String name, final Class<T> type, final String validatorPattern) {
    this.name = name;
    this.type = type;
    if (null != validatorPattern) {
      this.validator = Pattern.compile(validatorPattern);

    } else if (Integer.class == this.type || Long.class == this.type) {
      this.validator = NUMBER_PATTERN;
    } else if (Boolean.class == this.type) {
      this.validator = BOOLEAN_PATTERN;
    } else {
      this.validator = null;
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean match(final String valueStr) {
    if (null == valueStr) {
      return false;
    }
    if (null != this.validator) {
      final Matcher matcher = this.validator.matcher(valueStr);
      return matcher.matches();
    }
    return true;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void matchEx(final String valueStr) throws ArgumentException {
    if (false == this.match(valueStr)) {
      if (null == valueStr) {
        throw ArgumentException.createEx(ErrorCode.PARAM_MISSED, "The value must not be null",
            this.name);
      }
      throw ArgumentException.createEx(ErrorCode.PARAM_BAD, "The value must match the pattern: "
          + this.validator.pattern(), this.name);
    }
  }

  /**
   * Parse the parameters value from the given string value according to {@link #type}. Additional
   * the value is checked by {@link #matchEx(String)}.
   *
   * @param valueStr <i>optional</i> - the string value to parse the value from
   * @return the parsed value, may be <code>null</code>
   * @throws ArgumentException in case the parameter:
   *           <ul>
   *           <li>does not {@link #matchEx(String)} the {@link #validator}</li>
   *           <li>cannot be parsed according to {@link #type}</li>
   *           </ul>
   * @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
   *           programming error.
   */
  @Override
  public T parse(final String valueStr) throws ArgumentException, InternalServiceException {
    if (null == valueStr) {
      return null;
    }
    this.matchEx(valueStr);

    if (String.class == this.type) {
      return this.type.cast(valueStr);
    }
    if (Boolean.class == this.type) {
      return this.type.cast(Boolean.valueOf(("1".equals(valueStr)) || Boolean.valueOf(valueStr)));
    }
    try {
      if (Integer.class == this.type) {
        return this.type.cast(Integer.valueOf(valueStr));
      }
      if (Long.class == this.type) {
        return this.type.cast(Long.valueOf(valueStr));
      }
    } catch (final NumberFormatException e) {
      throw ArgumentException.createEx(ErrorCode.PARAM_BAD, "The value cannot be parsed as "
          + this.type.getSimpleName().toLowerCase() + ".", this.name);
    }

    return this.parseOther(valueStr);
  }

  /**
   * Field access for {@link #name}.
   *
   * @return the value of {@link #name}.
   */
  @Override
  public String getName() {
    return this.name;
  }

  /**
   * Field access for {@link #type}.
   *
   * @return the value of {@link #type}.
   */
  @Override
  public Class<T> getType() {
    return this.type;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final String toString(final Object value) throws InternalServiceException {
    if (false == this.type.isAssignableFrom(value.getClass())) {
      throw new InternalServiceException(ErrorCode.PANIC,
          "Parameter.toString(): Bad type of value. Expected {0} but is {1}.", this.type.getName(),
          value.getClass().getName());
    }
    if (String.class == this.type || Integer.class == this.type || Long.class == this.type) {
      return String.valueOf(value);
    }
    if (Boolean.class == this.type) {
      return Boolean.TRUE.equals(value) ? "1" : "0";
    }

    return this.toStringOther(value);
  }

  /**
   * Parse parameter values of other (non standard types). This method is called by
   * {@link #parse(String)} in case {@link #type} is none of the supported standard types (currently
   * String, Boolean, Integer and Long). It is intended for extensions.
   * <dl>
   * <dt style="margin-top:0.25cm;"><i>Note:</i>
   * <dd>This default implementation always throws an InternalServiceException.
   * </dl>
   *
   * @param valueStr <i>mandatory</i> - the string value to parse the value from
   * @return the parsed value, may be <code>null</code>
   * @throws ArgumentException in case the parameter cannot be parsed according to {@link #type}
   * @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
   *           programming error.
   */
  protected T parseOther(final String valueStr) throws ArgumentException, InternalServiceException {
    throw new InternalServiceException(ErrorCode.PANIC,
        "ParameterImpl.parseOther(): Unsupported parameter type: " + this.type.getName());
  }

  /**
   * Convert the values of other (non standard types) to their external form. This method is called
   * by {@link #toString(Object)} in case {@link #type} is none of the supported standard types
   * (currently String, Boolean, Integer and Long). It is intended for extensions.
   * <dl>
   * <dt style="margin-top:0.25cm;"><i>Note:</i>
   * <dd>This default implementation always throws an InternalServiceException.
   * </dl>
   *
   * @param value <i>mandatory</i> - the parameters value
   * @return the external form of the parameters value, never <code>null</code>
   * @throws InternalServiceException in case the given <code>value</code> does not match
   *           {@link #getClass()}
   */
  protected String toStringOther(final Object value) throws InternalServiceException {
    throw new InternalServiceException(ErrorCode.PANIC,
        "ParameterImpl.toStringOther(): Unsupported parameter type: " + this.type.getName());
  }
}

코드화 방법은 다음과 같습니다.

// enum A { a, b, c }
static final Set<Short> enumA = new LinkedHashSet<>(Arrays.asList(new Short[]{'a','b','c'}));

// enum B extends A { d }
static final Set<Short> enumB = new LinkedHashSet<>(enumA);
static {
    enumB.add((short) 'd');
    // If you have to add more elements:
    // enumB.addAll(Arrays.asList(new Short[]{ 'e', 'f', 'g', '♯', '♭' }));
}

LinkedHashSet는 각 엔트리가 1회만 존재하고 순서가 유지되는 것을 모두 제공합니다.순서가 문제가 되지 않으면HashSet대신.Java에서는 다음 코드를 사용할 수 없습니다.

for (A a : B.values()) { // enum B extends A { d }
    switch (a) {
        case a:
        case b:
        case c:
            System.out.println("Value is: " + a.toString());
        break;
        default:
            throw new IllegalStateException("This should never happen.");
    }
}

코드는 다음과 같이 작성할 수 있습니다.

for (Short a : enumB) {
    switch (a) {
        case 'a':
        case 'b':
        case 'c':
            System.out.println("Value is: " + new String(Character.toChars(a)));
        break;
        default:
            throw new IllegalStateException("This should never happen.");
    }
}

Java 7 이후로는 다음 버전에서도String:

// enum A { BACKWARDS, FOREWARDS, STANDING }
static final Set<String> enumA = new LinkedHashSet<>(Arrays.asList(new String[] {
        "BACKWARDS", "FOREWARDS", "STANDING" }));

// enum B extends A { JUMP }
static final Set<String> enumB = new LinkedHashSet<>(enumA);
static {
    enumB.add("JUMP");
}

Enum 치환 사용:

for (String a : enumB) {
    switch (a) {
        case "BACKWARDS":
        case "FOREWARDS":
        case "STANDING":
            System.out.println("Value is: " + a);
        break;
        default:
            throw new IllegalStateException("This should never happen.");
    }
}

언급URL : https://stackoverflow.com/questions/1414755/can-enums-be-subclassed-to-add-new-elements

반응형