![](/img/trans.png)
[英]What'd be the synthetically correct way of imitating extendable enum behavior?
[英]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"
除非你使用真正的enum
或SOME_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.