我想采取一个现有的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 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.");
}
}
其他回答
这就是我如何增强枚举继承模式运行时检查在静态初始化器。 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 ;
}
}
我倾向于避免枚举,因为它们是不可扩展的。继续以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值!如何为子类创建一个枚举,包括父类的枚举及其新值。
使用接口并不能真正解决问题:您可能会意外地获得重复的enum值。不可取的。
我最终只是组合了枚举:这确保了不会有任何重复的值,但代价是与相关类的绑定不那么紧密。但是,我认为重复的问题是我主要担心的……
枚举表示可能值的完整枚举。所以(毫无帮助的)答案是否定的。
举一个实际问题的例子,工作日,周末,工会,一周的天数。我们可以在“天-周”中定义所有的天,但这样就不能表示“工作日”和“周末-天”的特殊属性。
我们可以做的是,有三种枚举类型,并在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/类,可以添加更多常量。大致是这样的:
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)这样的东西,你必须自己实现它。