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

enum A {a,b,c}

enum B extends A {d}

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

这在Java中可行吗?


当前回答

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

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

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

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

其他回答

我的编码方式如下:

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

为了帮助理解为什么在语言实现级别上扩展Enum是不合理的,请考虑如果将扩展Enum的实例传递给只理解基本Enum的例程会发生什么。编译器承诺覆盖所有情况的开关实际上并不覆盖那些扩展的Enum值。

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

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

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

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

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

实际上,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…这对你来说可能并不重要!