我想采取一个现有的enum,并添加更多的元素,如下所示:
enum A {a,b,c}
enum B extends A {d}
/*B is {a,b,c,d}*/
这在Java中可行吗?
我想采取一个现有的enum,并添加更多的元素,如下所示:
enum A {a,b,c}
enum B extends A {d}
/*B is {a,b,c,d}*/
这在Java中可行吗?
当前回答
如果你错过了,在Joshua Bloch的书“Effective Java,第二版”中有一章。
第6章-枚举和注释 项目34:用接口模拟可扩展枚举
结论是:
A minor disadvantage of the use of interfaces to emulate extensible enums is those implementations cannot be inherited from one enum type to another. In the case of our Operation example, the logic to store and retrieve the symbol associated with an operation is duplicated in BasicOperation and ExtendedOperation. In this case, it doesn’t matter because very little code is duplicated. If there were a a larger amount of shared functionality, you could encapsulate it in a helper class or a static helper method to eliminate the code duplication.
总之,虽然不能编写可扩展枚举类型,但可以编写 通过编写接口来模拟实现的基本枚举类型 接口。这允许客户端编写自己实现的枚举 接口。这些枚举可以用于基本枚举类型所在的任何地方 假设api是根据接口编写的。
其他回答
这就是我如何增强枚举继承模式运行时检查在静态初始化器。 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 ;
}
}
为了帮助理解为什么在语言实现级别上扩展Enum是不合理的,请考虑如果将扩展Enum的实例传递给只理解基本Enum的例程会发生什么。编译器承诺覆盖所有情况的开关实际上并不覆盖那些扩展的Enum值。
这进一步强调了Java Enum的值不是像C那样的整数,例如:要使用Java Enum作为数组索引,你必须显式地要求它的ordinal()成员,要给Java Enum一个任意的整数值,你必须为它添加一个显式的字段并引用命名的成员。
这不是对OP的愿望的评论,只是关于为什么Java永远不会做到这一点。
不,在Java中不能这样做。除此之外,d可能是A的一个实例(考虑到“extends”的正常思想),但只知道A的用户不会知道它——这就违背了枚举是一组已知值的观点。
如果你能告诉我们更多关于你想如何使用它,我们可能会建议其他解决方案。
我自己也有同样的问题,我想把我的观点发表出来。我认为这样做有几个激励因素:
您希望有一些相关的枚举代码,但在不同的类中。在我的例子中,我有一个基类,在一个相关的枚举中定义了几个代码。在以后的某一天(今天!)我想为基类提供一些新功能,这也意味着枚举的新代码。 派生类既支持基类的枚举,也支持它自己的枚举。没有重复的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()..
它可以用于:
包括关于处理逻辑的显式元数据,不需要记住 实现一些多继承 我们不想使用类结构,例如发送简短的状态消息