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);
}
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}
How it can be useful: 假设我们想要类似的东西:我们有活动,我们正在使用枚举 . 这些枚举可以通过类似的处理进行分组 . 如果我们有许多元素的操作,那么一些事件开始操作,一些事件只是步骤而另一个事件结束操作 . 要收集此类操作并避免长时间切换,我们可以按照示例对它们进行分组并使用:
// 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; };
}
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());
}
}
66
我的代码方式如下:
// 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中的 not possible :
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");
}
使用枚举替换:
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.");
}
}
15 回答
不,你不能用Java做到这一点 . 除了其他任何东西,
d
然后可能是A
的一个实例(给出"extends"的正常想法),但是只知道A
的用户不会知道它 - 这会使枚举成为众所周知的集合 . Value 观如果您可以告诉我们更多关于您希望如何使用它的信息,我们可以建议其他解决方案 .
枚举代表可能值的完整枚举 . 所以(无益的)答案是否定的 .
作为一个真实问题的一个例子,需要工作日,周末和工会,一周中的几天 . 我们可以定义星期几内的所有日期,但是我们无法在工作日和周末日表示特殊属性 .
我们可以做的是,有三种枚举类型,在工作日/周末 - 日和星期几之间进行映射 .
或者,我们可以为星期几开放一个开放式界面:
或者我们可以将两种方法结合起来:
建议的解决方案是extensible enum pattern .
这涉及创建一个界面并使用当前使用枚举的地方 . 然后使枚举实现接口 . 您可以通过使新的枚举也扩展接口来添加更多常量 .
在封面下,您的ENUM只是编译器生成的常规类 . 生成的类扩展
java.lang.Enum
. 您无法扩展生成的类的技术原因是生成的类是final
. 本主题讨论了它最终的概念性原因 . 但我会在讨论中添加机制 .这是一个测试枚举:
来自javap的结果代码:
可以想象,您可以自己键入此类并删除“最终” . 但编译器会阻止您直接扩展“java.lang.Enum” . 您可以决定不扩展java.lang.Enum,但是您的类及其派生类将不是java.lang.Enum的实例......这对您来说可能并不重要!
可以写成:
How it can be useful: 假设我们想要类似的东西:我们有活动,我们正在使用枚举 . 这些枚举可以通过类似的处理进行分组 . 如果我们有许多元素的操作,那么一些事件开始操作,一些事件只是步骤而另一个事件结束操作 . 要收集此类操作并避免长时间切换,我们可以按照示例对它们进行分组并使用:
例:
添加一些更高级的:
在上面,如果我们有一些失败(myEvent.is(State_StatusGroup.FAIL))然后迭代前面的事件,我们可以很容易地检查我们是否必须通过以下方式恢复汇款:
它可用于:
这是我如何找到如何将枚举扩展到其他枚举的方法,这是一种非常直接的方法:
你有一个常见常量的枚举:
然后你可以尝试以这种方式进行手动扩展:
当然,每次需要扩展常量时,都必须修改SubEnum文件 .
万一你错过了,有's a chapter in the excellent Joshua Bloch'的书“Java Effective, 2nd edition” .
第6章 - 枚举和注释
第34项: Emulate extensible enums with interfaces
提取here .
只是结论:
我倾向于避免使用枚举,因为它们不可扩展 . 要继续使用OP的示例,如果A在库中而B在您自己的代码中,则如果它是枚举,则不能扩展A.这就是我有时会替换枚举的方式:
有一些要避免的坑,请参阅代码中的注释 . 根据您的需要,这是枚举的可靠,可扩展的替代方案 .
这是我在静态初始化程序中使用运行时检查来增强枚举继承模式的方法 .
BaseKind#checkEnumExtender
检查"extending"枚举以完全相同的方式声明基本枚举的所有值,以便#name()
和#ordinal()
保持完全兼容 .仍然有复制粘贴用于声明值,但如果有人在基类中添加或修改了一个值而没有更新扩展值,程序就会快速失败 .
相互扩展的不同枚举的常见行为:
基本枚举,带验证方法:
扩展样本:
Based on @Tom Hawtin - tackline 回答我们添加交换机支持,
我建议你采取相反的方法 .
而不是扩展现有的枚举,创建一个更大的枚举并创建它的子集 . 例如,如果你有一个名为PET的枚举,你想将它扩展到ANIMAL,你应该这样做:
小心,宠物不是不可变的集合,您可能希望使用Guava或Java9以获得更高的安全性 .
我自己也有同样的问题,我想发表自己的观点 . 我认为有几个激励因素可以做这样的事情:
您想要一些相关的枚举代码,但是在不同的类中 . 在我的例子中,我有一个基类,在相关的枚举中定义了几个代码 . 稍后(今天!)我想为基类提供一些新功能,这也意味着枚举的新代码 .
派生类将支持其父类的基类' enum as well as its own. No duplicate enum values! So: how to have an enum for the subclass that includes the enum'及其新值 .
使用界面并没有真正削减它:您可能会意外地获得重复的枚举值 . 不可取 .
我最后只是组合了枚举:这确保不会有任何重复的值,代价是与其关联的类不太紧密地联系在一起 . 但是,我认为重复的问题是我的主要关注点......
为了理解为什么在语言实现级别扩展Enum是不合理的,以考虑如果将扩展的Enum的实例传递给只能理解基本枚举的例程会发生什么 . 编译器承诺涵盖所有案例的开关实际上不会覆盖那些扩展的Enum值 .
这进一步强调Java Enum值不是像C这样的整数,例如:要使用Java Enum作为数组索引,必须明确要求其ordinal()成员,给java Enum一个必须添加的任意整数值一个名为member的显式字段和引用 .
这不是对OP的愿望的评论,只是为什么Java永远不会这样做 .
希望这篇优雅的解决方案能够在这篇长篇文章中看到我的同事,我想分享这种方法,用于继承接口方法之后的子类化 .
请注意,我们在此处使用自定义例外,除非您将其替换为异常,否则此代码将无法编译 .
文档很广泛,我希望对大多数人来说这是可以理解的 .
每个子类枚举需要实现的接口 .
实现ENUM基类 .
从基类“继承”的子类ENUM .
最后通用ParameterImpl添加一些实用程序 .
我的代码方式如下:
LinkedHashSet
提供每个条目仅存在一次,并保留其顺序 . 如果订单无关紧要,您可以改用HashSet
. 以下代码是Java中的 not possible :代码可以写成如下:
从Java 7开始,您甚至可以使用
String
执行相同的操作:使用枚举替换: