簡體   English   中英

使用相同方法的多個Java 8枚舉

[英]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在每個類中執行一次,該類特別設計用於有效地緩存每個類的元數據,線程安全並且在重要的環境中不會阻止類的卸載。 實際的toDBfromDB簡化為哈希查找。

順便說一句,此代碼使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM