使用相同方法的多个Java 8枚举
我有一系列的枚举,除了名称和值不同外,它们看起来像这样:使用相同方法的多个Java 8枚举,java,enums,java-8,Java,Enums,Java 8,我有一系列的枚举,除了名称和值不同外,它们看起来像这样: /* Bone Diagnosis. Value is internal code stored in database. */ public enum BoneDiagnosis { NORMAL(121), ELEVATED(207), OSTEOPENIA(314), OSTEOPOROSIS(315); private int value; BoneDiagnosis(final
/* Bone Diagnosis. Value is internal code stored in database. */
public enum BoneDiagnosis {
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
private int value;
BoneDiagnosis(final int value) {
this.value = value;
}
/** Get localized text for the enumeration. */
public String getText() {
return MainProgram.localize(this.getClass().getSimpleName().toUpperCase() + ".VALUE." + this.name());
}
/** Convert enumeration to predetermined database value. */
public int toDB() {
return value;
}
/** Convert a value read from the database back into an enumeration. */
public static BoneDiagnosis fromDB(final Integer v) {
if (v != null) {
for (final BoneDiagnosis pc : values()) {
if (v == pc.toDB()) {
return pc;
}
}
}
return null;
}
}
我知道我不能扩展枚举,但是是否有某种方法来抽象这个设计,以删除每个类在toDB()、fromDB()和getText()中的所有重复代码?我看了其他问题,比如有一个使用接口的示例,但我不知道如何处理构造函数和静态方法。我也不知道如何在fromDB()方法中删除对BoneDiagnosis类型的显式引用
我的梦想是让每个类只定义如下所示的内容,所有其他支持都包含在BoneDiagnosisComplexTypeDefinition中。这可能吗
public enum BoneDiagnosisComplexTypeDefinition {
NORMAL(121),
ELEVATED(207);
OSTEOPENIA(314),
OSTEOPOROSIS(315)
}
这个解决方案仍然有一些样板桥接代码 使用
接口
定义调用接口并在某些类中实现公共代码。
下面是一个简单的例子:
文件:BoneDiagnosis.java
public enum BoneDiagnosis
implements
CommonStuffs
{
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
private CommonStuffsImpl commonStuffsImpl;
private int value;
BoneDiagnosis(final int theValue)
{
value = theValue;
commonStuffsImpl = new CommonStuffsImpl();
}
@Override
public int toDB()
{
return commonStuffsImpl.toDBImplementation(value);
}
}
文件:commontuffs.java
public interface CommonStuffs
{
int toDB();
}
文件:CommonStuffsImpl.java
public class CommonStuffsImpl
{
public int toDBImplementation(
final int value)
{
return value;
}
}
接口方法是唯一的出路。下面是另一种利用接口最小化枚举中代码重复的方法 更新: 一些人抱怨反思和铸造。这里有两个不需要铸造的选项和一个不需要反射的选项 选项1(清除无反射,但需要枚举中的额外方法):
public static interface BoneDiagnosisType{
public String name();
public int getValue();
default int toDB() {
return getValue();
}
default String getText(){
return MainProgram.localize( this.getClass().getSimpleName().toUpperCase() + ".VALUE." + name() );
}
public static < E extends Enum<E> & BoneDiagnosisType > E fromDB(Class<E> eClass, Integer v) {
if (v != null) {
for ( final E pc : eClass.getEnumConstants() ) {
if ( v == pc.toDB() ) {
return pc;
}
}
}
return null;
}
}
public static enum BoneDiagnosis1 implements BoneDiagnosisType{
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
int value;
BoneDiagnosis1(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
public static enum BoneDiagnosis2 implements BoneDiagnosisType{
NORMAL(1121),
ELEVATED(1207),
OSTEOPENIA(1314),
OSTEOPOROSIS(1315);
int value;
BoneDiagnosis2(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
public static interface BoneDiagnosisType{
public String name();
default int toDB() {
try{
Class<?> clazz = getClass();
Field field = clazz.getDeclaredField("value");
return field.getInt(this);
}catch(RuntimeException e){
throw e;
}catch(Exception e){
throw new RuntimeException(e);
}
}
default String getText(){
return MainProgram.localize( this.getClass().getSimpleName().toUpperCase() + ".VALUE." + name() );
}
public static < E extends Enum<E> & BoneDiagnosisType > E fromDB(Class<E> eClass, Integer v) {
if (v != null) {
for ( final E pc : eClass.getEnumConstants() ) {
if ( v == pc.toDB() ) {
return pc;
}
}
}
return null;
}
}
public static enum BoneDiagnosis1 implements BoneDiagnosisType{
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
int value;
BoneDiagnosis1(int value) {
this.value = value;
}
}
public static enum BoneDiagnosis2 implements BoneDiagnosisType{
NORMAL(1121),
ELEVATED(1207),
OSTEOPENIA(1314),
OSTEOPOROSIS(1315);
int value;
BoneDiagnosis2(int value) {
this.value = value;
}
}
将提供:
121:骨骼诊断1.VALUE.NORMAL:正常
207:骨骼诊断1.VALUE.升高:升高
1121:骨骼诊断2.VALUE.NORMAL:正常
1207:BONEDIAGNOSIS2.VALUE.hived:hived您可以使用
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)
public @interface DbId {
int value();
}
final class Helper extends ClassValue<Map<Object,Object>> {
static final Helper INSTANCE = new Helper();
@Override protected Map<Object, Object> computeValue(Class<?> type) {
Map<Object,Object> m = new HashMap<>();
for(Field f: type.getDeclaredFields()) {
if(f.isEnumConstant()) try {
Object constant = f.get(null);
Integer id = f.getAnnotation(DbId.class).value();
m.put(id, constant);
m.put(constant, id);
}
catch(IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
return Collections.unmodifiableMap(m);
}
}
public interface Common {
String name();
Class<? extends Enum<?>> getDeclaringClass();
default int toDB() {
return (Integer)Helper.INSTANCE.get(getDeclaringClass()).get(this);
}
default String getText() {
return MainProgram.localize(
getDeclaringClass().getSimpleName().toUpperCase() + ".VALUE." + name());
}
static <T extends Enum<T>&Common> T fromDB(Class<T> type, int id) {
return type.cast(Helper.INSTANCE.get(type).get(id));
}
}
测试示例
int id = BoneDiagnosis.OSTEOPENIA.toDB();
System.out.println("id = " + id);
BoneDiagnosis d = Common.fromDB(BoneDiagnosis.class, id);
System.out.println("text = " + d.getText());
请注意,使用ClassValue
对每个类只执行一次反射操作,这是专门为高效、线程安全地缓存每个类元数据而设计的,并且不会在重要的环境中阻止类卸载。实际的toDB
和fromDB
被简化为散列查找
顺便说一句,这段代码使用
getDeclaringClass()
而不是getClass()
,因为枚举可能具有类似于enum Foo{BAR{…}…}
wheregetClass()的专门化
返回专门化类,而不是enum
类型。这里有另一种方法,可以使用一些人称之为“虚拟字段模式”的方法来实现。它将每个枚举的重复代码量减少到一个getter和一个字段。它还避免了反射
首先为枚举的所有公共方法创建一个接口。在本例中,我们将其称为Common
public interface Common {
int getId();
String getName();
}
public class CommonImpl implements Common {
private int id;
private String name;
public CommonImpl(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
}
public enum EnumImpl implements VirtualCommon {
ALPHA(1, "Alpha"),
BETA(2, "Beta"),
DELTA(3, "Delta"),
GAMMA(4, "Gamma");
private final Common common;
EnumImpl(int id, String name) {
this.common = new CommonImpl(id, name);
}
@Override
public Common getCommon() {
return this.common;
}
}
创建另一个扩展Common
的接口。这将只有一个抽象方法,它只返回Common
的实例。为其他方法指定一个默认实现,该实现将委托给公共
实例
public interface VirtualCommon extends Common {
Common getCommon();
@Override
default int getId() {
return getCommon().getId();
}
@Override
default String getName() {
return getCommon().getName();
}
}
现在创建Common
的具体实现
public interface Common {
int getId();
String getName();
}
public class CommonImpl implements Common {
private int id;
private String name;
public CommonImpl(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
}
public enum EnumImpl implements VirtualCommon {
ALPHA(1, "Alpha"),
BETA(2, "Beta"),
DELTA(3, "Delta"),
GAMMA(4, "Gamma");
private final Common common;
EnumImpl(int id, String name) {
this.common = new CommonImpl(id, name);
}
@Override
public Common getCommon() {
return this.common;
}
}
或者,如果您想保存几行代码,而不是CommonImpl
,您可以在Common
上放置一个返回匿名类的静态方法
static Common of(final int id, final String name) {
return new Common() {
@Override
public int getId() {
return id;
}
@Override
public String getName() {
return name;
}
};
}
现在您可以创建每个枚举,它们只需要一个字段和一个getter。实现VirtualCommon
的任何类或枚举都将成为Common
,因为它具有aCommon
public interface Common {
int getId();
String getName();
}
public class CommonImpl implements Common {
private int id;
private String name;
public CommonImpl(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
}
public enum EnumImpl implements VirtualCommon {
ALPHA(1, "Alpha"),
BETA(2, "Beta"),
DELTA(3, "Delta"),
GAMMA(4, "Gamma");
private final Common common;
EnumImpl(int id, String name) {
this.common = new CommonImpl(id, name);
}
@Override
public Common getCommon() {
return this.common;
}
}
“更喜欢组合而不是继承”。创建一个
EnumIndexTransformer
类,您可以将其组合到enum
中,然后委托给该类。您的枚举
还可以实现一个接口
-具有一个公开这些方法的可索引的
。您的代码将不会编译。这个必须是骨质疏松症之后,而不是之后ELEVATED@DwB抱歉,我在咀嚼代码以保护无辜者时引入了一个输入错误,clientI建议,如果您有一个广泛的枚举层次结构,更好的模式可能是创建一个具有您所需的层次结构行为的实体层次结构,并将值存储在持久性存储中。这很好。在高层次上,为什么需要将eClass作为fromDB()的参数?它与其他的有什么不同?像BoneDiagnosis2.fromDB(intval)这样的东西不可能吗?您能解释一下需要“”吗?BoneDiagnosisType已经扩展了类型“E extends Enum”,那么为什么需要这两个边界呢?我们需要eClass的原因是该方法是静态的,我们无法知道要调用哪个Enum值
方法。通过使用该类,我们可以使用class.getEnumConstants
执行值所做的操作。我们在泛型中需要这两种类型的原因是,因为我们使用的是不是enum的BoneDiagnosisType
接口(我们无法扩展enum)。使用这两种方法将确保class.getEnumConstants
将提供BoneDiagnosisType,而无需强制转换;乍一看,由于它是一个接口,我感到困惑,认为fromDB()是BONEDIAGNOSIS2类型中的一个静态实例,也是BONEDIAGNOSIS1类型中的另一个实例,但它只是BoneDiagnosisType的一个静态部分。谢谢这充斥着糟糕的编程实践:使用setAccessible
;反射的使用,enum类的编写者看不见;使用“catch(Exception e)”代替catch(ReflectiveOperationException e)
;使用(E)this
而不仅仅是+this
(以及由此产生的对@SuppressWarnings的需求)。这在功能上可能是正确的,但这是一个糟糕的解决方案。@simpleuser。我确实更新了我的答案,有两个选项不需要强制转换,还有一个选项也不需要反射,是首选方法。我仍然发现