簡體   English   中英

在java中創建可擴展Enum行為的最佳方法

[英]Best way to create the behavior of an extendable Enum in java

我想創建類似於可擴展Enum的東西(理解在Java 6中不可能擴展Enums)。

這是我想要做的:
我有許多“模型”類,每個類都有一組與之關聯的字段。 這些字段用於索引包含數據表示的地圖。

我需要能夠從類OR實例obj訪問Fields,如下所示:

MyModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"

要么

myModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"

我還需要能夠獲得Model的所有字段列表

MyModel.Fields.getKeys() #=> List<String> of all the string values ("diff-from-field name")

在為每個Model定義“Fields”類時,我希望能夠將定義保存在與Model相同的文件中。

public class MyModel {
    public static final Fields extends BaseFields { 
        public static final String SOME_FIELD = "diff-from-field-name";
        public static final String FOO = "bar";
    }

    public Fields Fields = new Fields();

    // Implement MyModel logic
}

我還想讓OtherModel擴展MyModel並且可以從MyModel.Fields繼承Fields,然后在它頂部添加自己的Fields ...

public class OtherModel extends MyModel {
   public static final class Fields extends MyModel.Fields { 
        public static final String CAT = "feline";
        ....

哪會允許

OtherModel.Fields.CAT #=> feline
OtherModel.Fields.SOME_FIELD #=> diff-from-field-name
OtherModel.Fields.FOO #=> bar
OtherModel.Fields.getKeys() #=> 3 ["feline", "diff-from-field-name", "bar"]

我試圖讓模型中的“Fields”的定義盡可能簡潔和簡單,因為各種開發人員將構建這些“Model”對象。

謝謝

我需要能夠從類OR實例obj訪問Fields,如下所示:

  MyModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"

除非你使用真正的enumSOME_FIELD是一個真實的領域,否則這在Java中是不可能的。 在任何一種情況下,“枚舉”都不是可擴展的。

您在Java 6中可以做的最好的事情是將枚舉建模為從String名稱到int值的映射。 這是可擴展的,但是從名稱到值的映射會導致運行時成本......以及您的代碼將使用不是枚舉成員的名稱的可能性。


Java中enum類型不可擴展的原因是擴展enum會破壞原始enum的隱式不變量,因此(因此)不能替代。

我能夠提出一個使用反射似乎有效的解決方案 - 我還沒有經歷過全部測試,這更像是我在看看我有什么可能的選擇。

ActiveField:所有其他“Fields”類(將成為我的Model類中的內部類)的Java類將擴展。 這有一個非靜態方法“getKeys()”,它查看“this”的類,並從中提取所有Fields的列表。 然后檢查修改器,字段類型和套管等一些內容,以確保它只查看符合我慣例的字段:所有“字段鍵”必須是String類型的“public static final”,字段名稱必須全部大寫。

public class ActiveField {
private final String key;

protected ActiveField() {
    this.key = null;
}

public ActiveField(String key) {
    System.out.println(key);
    if (key == null) {
        this.key = "key:unknown";
    } else {
        this.key = key;
    }
}

public String toString() {
    return this.key;
}

@SuppressWarnings("unchecked")
public List<String> getKeys() {
    ArrayList<String> keys = new ArrayList<String>();
    ArrayList<String> names = new ArrayList<String>();

    Class cls;
    try {
        cls = Class.forName(this.getClass().getName());
    } catch (ClassNotFoundException e) {
        return keys;
    }

    Field fieldList[] = cls.getFields();

    for (Field fld : fieldList) {
        int mod = fld.getModifiers();

        // Only look at public static final fields
        if(!Modifier.isPublic(mod) || !Modifier.isStatic(mod) || !Modifier.isFinal(mod)) {
            continue;
        }

        // Only look at String fields
        if(!String.class.equals(fld.getType())) {
            continue;
        }

        // Only look at upper case fields
        if(!fld.getName().toUpperCase().equals(fld.getName())) {
            continue;
        }

        // Get the value of the field
        String value = null;
        try {
            value = StringUtils.stripToNull((String) fld.get(this));
        } catch (IllegalArgumentException e) {
            continue;
        } catch (IllegalAccessException e) {
            continue;
        }

        // Do not add duplicate or null keys, or previously added named fields
        if(value == null || names.contains(fld.getName()) || keys.contains(value)) {
            continue;
        }

        // Success! Add key to key list
        keys.add(value);
        // Add field named to process field names list
        names.add(fld.getName());

    }

    return keys;
}

public int size() {
    return getKeys().size();
} 
}

然后在我的“模型”類中(它們是圍繞Map的花哨包裝器,可以使用Fields字段編制索引)

public class ActiveResource {/ ** *用於建模ActiveResource objs的基本字段 - 從* ActiveResource繼承的所有類都應具有這些字段/值(除非被覆蓋)* / public static class字段擴展ActiveField {public static final String CREATED_AT =“node:創建“; public static final String LAST_MODIFIED_AT =“node:lastModified”; }

public static final Fields Fields = new Fields();

    ... other model specific stuff ...

}

然后我可以創建一個擴展我的ActiveResource類的類Foo

public class Foo extends ActiveResource {

public static class Fields extends ActiveResource.Fields {
    public static final String FILE_REFERENCE = "fileReference";
    public static final String TYPE = "type";
}

public static final Fields Fields = new Fields();

    ... other Foo specific stuff ...

現在,我可以做到以下幾點:

ActiveResource ar = new ActiveResource().
Foo foo = new Foo();

ar.Fields.size() #=> 2
foo.Fields.size() #=> 4

ar.Fields.getKeys() #=> ["fileReference", "type", "node:created", "node:lastModified"]
foo.Fields.getKeys() #=> ["node:created", "node:lastModified"]


ar.Fields.CREATED_AT #=> "node:created"
foo.Fields.CREATED_AT #=> "node:created"
foo.Fields.TYPE #=> "type"
etc.

我還可以從我的Model對象中訪問Fields作為靜態字段

Foo.Fields.size(); Foo.Fields.getKeys(); Foo.Fields.CREATED_AT; Foo.Fields.FILE_REFERENCE;

到目前為止,這看起來是一個非常好的解決方案,需要最少的指令來構建新的模型。

我剛嘗試了一些代碼試圖做你剛剛描述的內容而且它真的很麻煩。

如果你在模型類中的某個地方有一個Fields靜態內部類,如下所示:

public class Model {

  public static class Fields {

    public static final String CAT = "cat";
    protected static final List<String> KEYS = new ArrayList<String>();

    static {
      KEYS.add(CAT);
    }

    protected Fields() {}

    public static List<String> getKeys() {
      return Collections.unmodifiableList(KEYS);
    }
  }
}

你像這樣擴展這個類:

public class ExtendedModel extends Model {

  public static class ExtendedFields extend Model.Fields {

    public static final String DOG = "dog";

    static {
      KEYS.add(DOG);
    }

    protected ExtendedFields() {}
  }
}

那是對的錯。 如果你調用Model.Fields.getKeys()你會得到你所期望的: [cat] ,但如果你調用ExtendedModel.ExtendedFields.getKeys()你會得到相同的: [cat] ,沒有dog 原因: getKeys()Model.Fields的靜態成員,調用ExtendedModel.ExtendedFields.getKeys()錯誤的,因為你真的在那里調用Model.Fields.getKeys()

因此,您要么使用實例方法操作,要么在所有Fields子類中創建靜態getKeys()方法,這是我甚至無法描述的錯誤。


也許您可以創建一個Field接口,客戶端可以實現該接口並插入您的模型。

public interface Field {
  String value();
}

public class Model {

  public static Field CAT = new Field() { 
    @Override public String value() {
      return "cat";
    }
  };

  protected final List<Field> fields = new ArrayList();

  public Model() {
    fields.add(CAT);
  }

  public List<Field> fields() {
    return Collections.unmodifiableList(fields);
  }      
}

public class ExtendedModel extends Model {

  public static Field DOG= new Field() { 
    @Override public String value() {
      return "dog";
    }
  };

  public ExtendedModel() {
    fields.add(DOG);
  } 
}

我想知道你是否真的需要生成的字段枚舉。 如果要根據模型生成列表的enum ,為什么不生成列出所有字段及其類型的類? 即,它比靜態或動態生成的枚舉更難生成類,它更高效,更靈活,編譯友好。

所以你可以從模型中生成類似的東西

class BaseClass { // with BaseField
    String field;
    int number;
}

class ExtendedClass extends BaseClass { // with OtherFields
    String otherField;
    long counter;
}

發明自己的類型系統真的有好處嗎?

詛咒 - 出於某種原因,我對我提出的解決方案的非常冗長的反應沒有發布。

我將簡要概述一下,如果有人想要更多細節,我可以在我有更多時間/耐心時重新發布。

我創建了一個java類(稱為ActiveField),所有內部Fields都繼承該類。 每個內部字段類都定義了一系列字段:

public static class Fields extends ActiveField {  
     public static final String KEY = "key_value";  
}

在ActiveRecord類我有一個非靜態方法getKeys() ,它使用反射來看看在所有字段this ,通過迭代,得到他們的價值和將它們作為一個列表。

它似乎工作得很好 - 如果您對更完整的代碼示例感興趣,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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