[英]Multiple Java 8 Enums with same methods
我有一系列看起來像這樣的枚舉,只是名稱和值不同:
/* 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()中的所有重復代碼嗎? 我看了其他問題,例如Java 8是否可以擴展枚舉? 上面有一個使用接口的示例,但是我不知道如何處理構造函數和靜態方法。 我也無法弄清楚如何在fromDB()方法中刪除對BoneDiagnosis類型的顯式引用。
我的夢想是僅對每個類進行如下定義,而所有其他支持都封裝在BoneDiagnosisComplexTypeDefinition中。 這可能嗎?
public enum BoneDiagnosisComplexTypeDefinition {
NORMAL(121),
ELEVATED(207);
OSTEOPENIA(314),
OSTEOPOROSIS(315)
}
你可以盡量減少per- enum
使用代碼和每個操作的開銷
@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));
}
}
public enum BoneDiagnosis implements Common {
@DbId(121) NORMAL,
@DbId(207) ELEVATED,
@DbId(314) OSTEOPENIA,
@DbId(315) OSTEOPOROSIS;
}
測試例
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 { … } … }
,其中getClass()
返回特殊化類而不是enum
類型。
界面方法是唯一的方法。 這是利用接口來最小化枚舉中的代碼重復的另一種方法。
更新:
有些人抱怨反射和投射。 這是兩種不需要鑄造的選項,一種不需要反射。
選項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;
}
}
Option2(要求反射使枚舉盡可能簡單):
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;
}
}
和示例打印輸出:
System.out.println( BoneDiagnosis1.NORMAL.toDB() + " : " + BoneDiagnosis1.NORMAL.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis1.class, 121 ) );
System.out.println( BoneDiagnosis1.ELEVATED.toDB() + " : " + BoneDiagnosis1.ELEVATED.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis1.class, 207 ) );
System.out.println( BoneDiagnosis2.NORMAL.toDB() + " : " + BoneDiagnosis2.NORMAL.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis2.class, 1121 ) );
System.out.println( BoneDiagnosis2.ELEVATED.toDB() + " : " + BoneDiagnosis2.ELEVATED.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis2.class, 1207 ) );
會給:
121:BONEDIAGNOSIS1.VALUE.NORMAL:正常
207:骨診斷1.值。提升:提升
1121:BONEDIAGNOSIS2.VALUE.NORMAL:正常
1207:BONEDIAGNOSIS2.VALUE.ELEVATED:提升
這是您可以使用所謂的“虛擬場模式”進行處理的另一種方法。 它將每個枚舉的重復代碼數量減少到一個getter和一個field。 它還避免了反射。
首先為您的枚舉共有的所有方法創建一個接口。 在此示例中,我們將其稱為Common
。
public interface Common {
int getId();
String getName();
}
創建另一個擴展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 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;
}
}
或者,如果您想保存幾行代碼,而不是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;
}
};
}
現在,您可以創建每個枚舉,而它們只需要一個字段和一個吸氣劑。 實現VirtualCommon
任何類或枚舉都將是Common
因為它具有 Common
。
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;
}
}
該解決方案仍然具有一些樣板橋接代碼。
使用Interface
定義調用接口並在某些類中實現通用代碼。 這是一個簡單的示例:
文件: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);
}
}
文件:CommonStuffs.java
public interface CommonStuffs
{
int toDB();
}
文件:CommonStuffsImpl.java
public class CommonStuffsImpl
{
public int toDBImplementation(
final int value)
{
return value;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.