在java中创建可扩展枚举行为的最佳方法

在java中创建可扩展枚举行为的最佳方法,java,class,inheritance,inner-classes,extends,Java,Class,Inheritance,Inner Classes,Extends,我想创建一些类似于可扩展枚举的东西(在Java6中不可能理解扩展枚举) 下面是我想做的: 我有许多“模型”类,每个类都有一组与之关联的字段。这些字段用于索引到包含数据表示的地图中 我需要能够从类或实例obj访问字段,如下所示: MyModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name" 或 我还需要能够得到模型的所有字段的列表 MyModel.Fields.getKeys() #=> List&l

我想创建一些类似于可扩展枚举的东西(在Java6中不可能理解扩展枚举)

下面是我想做的:
我有许多“模型”类,每个类都有一组与之关联的字段。这些字段用于索引到包含数据表示的地图中

我需要能够从类或实例obj访问字段,如下所示:

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

我还需要能够得到模型的所有字段的列表

MyModel.Fields.getKeys() #=> List<String> of all the string values ("diff-from-field name")
我还想让OtherModel扩展MyModel,并能够从MyModel.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"]
我试图使模型中“字段”的定义尽可能清晰和简单,因为各种开发人员将构建这些“模型”对象


谢谢

我刚刚尝试了一些代码,试图实现您刚才描述的功能,这真的很麻烦

如果在模型类的某个地方有一个
字段
静态内部类,如下所示:

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]
,没有
狗。原因:
getKeys()
Model.Fields
的静态成员,调用
ExtendedModel.ExtendedFields.getKeys()
是错误的,因为您确实在那里调用了
Model.Fields.getKeys()

因此,您要么使用实例方法进行操作,要么在所有字段子类中创建一个静态的
getKeys()
方法,这是错误的,我甚至无法描述


也许您可以创建一个
字段
接口,您的客户可以实现该接口并将其插入到您的模型中

公共接口字段{
字符串值();
}
公共类模型{
公共静态字段CAT=新字段(){
@重写公共字符串值(){
返回“猫”;
}
};
受保护的最终列表字段=新ArrayList();
公共模型(){
字段。添加(CAT);
}
公共列表字段(){
返回集合。不可修改列表(字段);
}      
}
公共类扩展模型扩展模型{
公共静态字段DOG=新字段(){
@重写公共字符串值(){
返回“狗”;
}
};
公共扩展模型(){
字段。添加(狗);
} 
}
我需要能够从类或实例obj访问字段,如下所示:

MyModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"
这在Java中是不可能的,除非您使用实
enum
SOME_字段
是实字段。在这两种情况下,“枚举”都是不可扩展的

在Java6中,您最好将枚举建模为从字符串名称到
int
值的映射。这是可扩展的,但是从名称到值的映射会产生运行时成本。。。以及您的代码可能使用的名称不是枚举的成员



Java中的
enum
类型不可扩展的原因是扩展的
enum
会破坏原始
enum
的隐式不变量,因此无法替换。

我想知道您是否真的需要生成的字段枚举。如果要生成基于模型的字段列表的
enum
,为什么不生成一个列出所有字段及其类型的类呢?i、 生成类并不比静态或动态生成的枚举难多少,而且它更高效、灵活、编译器友好

所以你可以从一个模型中生成

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

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

发明自己的打字系统真的有好处吗?

诅咒——由于某种原因,我对我提出的解决方案的冗长回复没有发布

我只是粗略地概述一下,如果有人想要更多的细节,我可以在我有更多时间/耐心的时候重新发布

我创建了一个java类(称为ActiveField),所有内部字段都从中继承。 每个内部字段类都定义了一系列字段:

public static class Fields extends ActiveField {  
     public static final String KEY = "key_value";  
}
在ActiveRecord类中,我有一个非静态方法
getKeys()
,它使用反射来查看
this
上的所有字段,遍历它们,获取它们的值并将它们作为列表返回


它似乎工作得很好-如果您对更完整的代码示例感兴趣,请告诉我。

我能够使用反射找到一个似乎有效的解决方案——我还没有完成所有的测试,这更像是我在胡闹,看看我有什么可能的选择

ActiveField:所有其他“Fields”类(在我的模型类中是内部类)将扩展的Java类。这有一个非静态方法“getKeys()”,它查看“This”类,并从中提取所有字段的列表。然后,它检查一些东西,如修饰符、字段类型和大小写,以确保它只查看符合我的约定的字段:所有“字段键”必须是字符串类型的“publicstaticfinal”,字段名必须全部大写

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();
} 
}
}

然后我可以创建一个类Foo来扩展我的ActiveResource类

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.
我还可以将这些字段作为静态字段从模型对象中访问

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

到目前为止,这看起来是一个非常好的解决方案,它只需要很少的指导就可以构建新模型。

不要听起来粗鲁或陈词滥调,但如果语言的设计者决定枚举不能扩展,您可能需要搜索他们为什么做出这个决定,或者你可能会发现自己掉进了一个他们专门为开发人员设置的陷阱。这是公平的——我提到“枚举”的原因是因为它是我所知道的Java中最接近我的用例的构造。(该死的时间限制)这是公平的——我提到“枚举”是因为它是我所知道的最接近的构造
public static final Fields Fields = new Fields();

    ... other model specific stuff ...
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.
Foo.Fields.size(); Foo.Fields.getKeys(); Foo.Fields.CREATED_AT; Foo.Fields.FILE_REFERENCE;