枚举可以被子类化以添加新元素吗?

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

enum A {a,b,c}


enum B extends A {d}


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

这在Java中可行吗?

431236 次浏览

不,在Java中不能这样做。除此之外,d可能是A的一个实例(考虑到“扩展”的正常思想),但是只知道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)这样的东西,你必须自己实现它。

枚举表示可能值的完整枚举。所以(毫无帮助的)答案是否定的。

举一个实际问题的例子,工作日,周末,工会,一周的天数。我们可以在“天-周”中定义所有的天,但这样就不能表示“工作日”和“周末-天”的特殊属性。

我们可以做的是,有三种枚举类型,并在weekdays/weekend-days和days-of-week之间进行映射。

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

或者,我们可以为day-of-week提供一个开放式接口:

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值!如何为子类创建一个枚举,包括父类的枚举及其新值。

使用接口并不能真正解决问题:您可能会意外地获得重复的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. 我们不想使用类结构,例如发送简短的状态消息

我倾向于避免枚举,因为它们是不可扩展的。继续以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; };
}

有一些陷阱要避免,请参阅代码中的注释。根据您的需要,这是枚举的可靠、可扩展的替代方案。

实际上,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);
}

可以想象,您可以自己键入这个类,并删除“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,第二版"》中有一章。

  • 第6章-枚举和注释
  • 第34项:用接口模拟可扩展枚举

结论是:

使用接口来模拟可扩展枚举的一个小缺点是 这些实现不能从一个enum类型继承到另一个enum类型。在 在Operation的例子中,存储和检索相关符号的逻辑 在BasicOperation和ExtendedOperation中重复一个操作。 在本例中,这并不重要,因为复制的代码非常少。如果有 对于大量的共享功能,您可以将其封装在helper类或 消除代码重复的静态helper方法
总之,虽然不能编写可扩展枚举类型,但可以 通过编写接口来模拟实现的基本枚举类型 接口。这允许客户端编写自己实现的枚举 接口。这些枚举可以用于基本枚举类型所在的任何地方

.使用,假设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文件。

我的编码方式如下:

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

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

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

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

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

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

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

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是不合理的,请考虑如果将扩展Enum的实例传递给只理解基本Enum的例程会发生什么。编译器承诺覆盖所有情况的开关实际上并不覆盖那些扩展的Enum值。

这进一步强调了Java Enum的值不是像C那样的整数,例如:要使用Java Enum作为数组索引,你必须显式地要求它的ordinal()成员,要给Java Enum一个任意的整数值,你必须为它添加一个显式的字段并引用命名的成员。

这不是对OP的愿望的评论,只是关于为什么Java永远不会做到这一点。

我建议你采取另一种方式。

不要扩展现有枚举,而是创建一个更大的枚举,并创建它的一个子集。 例如,如果你有一个名为PET的枚举,你想把它扩展到ANIMAL,你应该这样做:

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

注意,pets不是一个不可变的集合,您可能会使用Guava或Java9来提高安全性。