Programing

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

lottogame 2020. 2. 12. 08:00
반응형

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


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

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가 인터페이스 측면에서 작성되었다고 가정하면 기본 열거 형 유형을 사용할 수있는 모든 위치에서이 열거 형을 사용할 수 있습니다.


나는 열거 할 수 없기 때문에 열거 형을 피하는 경향이 있습니다. OP의 예를 유지하기 위해 A가 라이브러리에 있고 B가 자신의 코드에 있으면 A가 열거 형이면 A를 확장 할 수 없습니다. 이것은 때때로 열거 형을 대체하는 방법입니다.

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

피해야 할 구덩이가 있습니다. 코드의 주석을 참조하십시오. 필요에 따라 열거 형에 대한 견고하고 확장 가능한 대안입니다.


이것이 정적 초기화 프로그램에서 런타임 검사를 통해 열거 상속 패턴을 향상시키는 방법입니다. BaseKind#checkEnumExtender열거 "연장"정확히 같은 방법으로 기본 열거의 모든 값을 선언하는 것을 확인 지금 #name()#ordinal()완벽하게 호환 남아있다.

값을 선언하는 데 여전히 복사-붙여 넣기가 필요하지만 누군가가 기본 값을 확장하거나 업데이트하지 않고 값을 추가하거나 수정하면 프로그램이 빠르게 실패합니다.

서로 다른 열거 형에 대한 일반적인 동작은 서로 확장됩니다.

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

}

@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:
}

나는 당신이 다른 방법으로 접근하는 것이 좋습니다.

기존 열거를 확장하는 대신 더 큰 열거 형을 만들고 하위 집합을 만듭니다. 예를 들어 PET라는 열거 형이 있고이를 ANIMAL로 확장하려는 경우 대신 다음을 수행해야합니다.

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을 배열 인덱스로 사용하려면 ordinal () 멤버를 명시 적으로 요청해야하며 Java Enum에 추가해야하는 임의의 정수 값을 제공해야합니다. 그것에 대한 명시 적 필드와 그 명명 된 멤버를 참조하십시오.

이것은 OP의 요구에 대한 의견이 아니며 Java가 결코 그렇게하지 않는 이유에 대한 의견입니다.


내 동료 의이 우아한 솔루션 이이 긴 게시물에서도 볼 수 있기를 희망하면서 인터페이스 접근법과 그 너머를 따르는 서브 클래스 링에 대한이 접근법을 공유하고 싶습니다.

여기서는 사용자 정의 예외를 사용하며이 코드를 예외로 바꾸지 않으면이 코드가 컴파일되지 않습니다.

설명서는 광범위하며 대부분의 사람들이 이해할 수 있기를 바랍니다.

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

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

마지막으로 일반 ParameterImpl을 사용하여 일부 유틸리티를 추가하십시오.

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각 항목이 한 번만 존재하고 순서가 유지되도록합니다. 순서가 중요하지 않으면 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");
}

열거 형 교체 사용 :

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



반응형