我想采取一个现有的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中可行吗?
当前回答
实际上,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…这对你来说可能并不重要!
其他回答
我倾向于避免枚举,因为它们是不可扩展的。继续以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 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()..
它可以用于:
包括关于处理逻辑的显式元数据,不需要记住 实现一些多继承 我们不想使用类结构,例如发送简短的状态消息
这是一种方法,我发现如何扩展一个枚举到其他枚举,是一个非常直接的方法:
假设你有一个包含公共常量的枚举:
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是不合理的,请考虑如果将扩展Enum的实例传递给只理解基本Enum的例程会发生什么。编译器承诺覆盖所有情况的开关实际上并不覆盖那些扩展的Enum值。
这进一步强调了Java Enum的值不是像C那样的整数,例如:要使用Java Enum作为数组索引,你必须显式地要求它的ordinal()成员,要给Java Enum一个任意的整数值,你必须为它添加一个显式的字段并引用命名的成员。
这不是对OP的愿望的评论,只是关于为什么Java永远不会做到这一点。
我自己也有同样的问题,我想把我的观点发表出来。我认为这样做有几个激励因素:
您希望有一些相关的枚举代码,但在不同的类中。在我的例子中,我有一个基类,在一个相关的枚举中定义了几个代码。在以后的某一天(今天!)我想为基类提供一些新功能,这也意味着枚举的新代码。 派生类既支持基类的枚举,也支持它自己的枚举。没有重复的enum值!如何为子类创建一个枚举,包括父类的枚举及其新值。
使用接口并不能真正解决问题:您可能会意外地获得重复的enum值。不可取的。
我最终只是组合了枚举:这确保了不会有任何重复的值,但代价是与相关类的绑定不那么紧密。但是,我认为重复的问题是我主要担心的……