我想采取一个现有的enum,并添加更多的元素,如下所示:

enum A {a,b,c}

enum B extends A {d}

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

这在Java中可行吗?


当前回答

实际上,ENUM只是编译器生成的一个常规类。生成的类扩展了java.lang.Enum。不能扩展生成的类的技术原因是生成的类是最终的。在这个主题中讨论了它是最终的概念原因。但我将在讨论中加入机制。

下面是一个测试枚举:

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

可以想象,您可以自己键入这个类,并删除“final”。但是编译器阻止你扩展“java.lang”。直接枚举”。你可以决定不扩展java.lang。Enum,但是这样你的类和它的派生类就不会是java.lang.Enum…这对你来说可能并不重要!

其他回答

我希望我的一个同事的这个优雅的解决方案能在这篇长文章中看到,我想分享这种继承接口方法的方法。

请注意,我们在这里使用自定义异常,除非您将其替换为您的异常,否则此代码将无法编译。

文档内容很广泛,我希望大多数人都能理解。

每个子类枚举都需要实现的接口。

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只是编译器生成的一个常规类。生成的类扩展了java.lang.Enum。不能扩展生成的类的技术原因是生成的类是最终的。在这个主题中讨论了它是最终的概念原因。但我将在讨论中加入机制。

下面是一个测试枚举:

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

可以想象,您可以自己键入这个类,并删除“final”。但是编译器阻止你扩展“java.lang”。直接枚举”。你可以决定不扩展java.lang。Enum,但是这样你的类和它的派生类就不会是java.lang.Enum…这对你来说可能并不重要!

这就是我如何增强枚举继承模式运行时检查在静态初始化器。 BaseKind的#checkEnumExtender检查“extends”enum是否以完全相同的方式声明了基enum的所有值,以便#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() ;
}

基准enum,带有验证方法:

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

}

不,在Java中不能这样做。除此之外,d可能是A的一个实例(考虑到“extends”的正常思想),但只知道A的用户不会知道它——这就违背了枚举是一组已知值的观点。

如果你能告诉我们更多关于你想如何使用它,我们可能会建议其他解决方案。

对此,推荐的解决方案是可扩展枚举模式。

这涉及到创建一个接口,并在当前使用枚举的位置使用该接口。然后使枚举实现接口。通过添加扩展接口的附加enum/类,可以添加更多常量。大致是这样的:

public interface TrafficLights {
  public abstract String getColour();
}
public enum StandardTrafficLights implements TrafficLights {
  RED, YELLOW, GREEN;
  public String getColour() {
    return name();
  }
}
public enum WeirdTrafficLights implements TrafficLights {
  DOUBLE_RED;
  public String getColour() {
    return name();
  }
}

注意,如果你想要TrafficLights.valueof(String)这样的东西,你必须自己实现它。