简体   繁体   English

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

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

I want to create something that resembles an extendable Enum (understanding extending Enums isn't possible in Java 6). 我想创建类似于可扩展Enum的东西(理解在Java 6中不可能扩展Enums)。

Here is what im trying to do: 这是我想要做的:
I have many "Model" classes and each of these classes have a set of Fields that are to be associated with it. 我有许多“模型”类,每个类都有一组与之关联的字段。 These Fields are used to index into Maps that contain representations of the data. 这些字段用于索引包含数据表示的地图。

I need to be able to access the Fields from an Class OR instance obj as follows: 我需要能够从类OR实例obj访问Fields,如下所示:

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

or 要么

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

I also need to be able to get a list of ALL the fields for Model 我还需要能够获得Model的所有字段列表

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

When defining the "Fields" class for each Model, I would like to be able to keep the definition in the same file as the Model. 在为每个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
}

I also want to have OtherModel extends MyModel and beable to inherit the Fields from MyModel.Fields and then add its own Fields on top if it .. 我还想让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";
        ....

Which woulds allow 哪会允许

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"]

I am trying to make the definition of the "Fields" in the models as clean and simple as possible as a variety of developers will be building out these "Model" objects. 我试图让模型中的“Fields”的定义尽可能简洁和简单,因为各种开发人员将构建这些“Model”对象。

Thanks 谢谢

I need to be able to access the Fields from an Class OR instance obj as follows: 我需要能够从类OR实例obj访问Fields,如下所示:

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

That is not possible in Java unless you use a real enum or SOME_FIELD is a real field. 除非你使用真正的enumSOME_FIELD是一个真实的领域,否则这在Java中是不可能的。 In either case, the "enum" is not extensible. 在任何一种情况下,“枚举”都不是可扩展的。

The best you can do in Java 6 is to model the enumeration as mapping from String names to int values. 您在Java 6中可以做的最好的事情是将枚举建模为从String名称到int值的映射。 That is extensible, but the mapping from names to values incurs a runtime cost ... and the possibility that your code will use a name that is not a member of the enumeration. 这是可扩展的,但是从名称到值的映射会导致运行时成本......以及您的代码将使用不是枚举成员的名称的可能性。


The reason that enum types in Java are not extensible is that the extended enum would break the implicit invariants of the original enum and (as a result) could not be substitutable. Java中enum类型不可扩展的原因是扩展enum会破坏原始enum的隐式不变量,因此(因此)不能替代。

I was able to come up with a solution using reflection that seems to work -- I haven't gone through the full gamut of testing, this was more me just fooling around seeing what possible options I have. 我能够提出一个使用反射似乎有效的解决方案 - 我还没有经历过全部测试,这更像是我在看看我有什么可能的选择。

ActiveField : Java Class which all other "Fields" Classes (which will be inner classes in my Model classes) will extend. ActiveField:所有其他“Fields”类(将成为我的Model类中的内部类)的Java类将扩展。 This has a non-static method "getKeys()" which looks at "this's" class, and pulled a list of all the Fields from it. 这有一个非静态方法“getKeys()”,它查看“this”的类,并从中提取所有Fields的列表。 It then checks a few things like Modifiers, Field Type and Casing, to ensure that it only looks at Fields that match my convention: all "field keys" must be "public static final" of type String, and the field name must be all UPPERCASE. 然后检查修改器,字段类型和套管等一些内容,以确保它只查看符合我惯例的字段:所有“字段键”必须是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();
} 
}

Then in my "Model" classes (which are fancy wrappers around a Map, which can be indexed using the Fields fields) 然后在我的“模型”类中(它们是围绕Map的花哨包装器,可以使用Fields字段编制索引)

public class ActiveResource { /** * Base fields for modeling ActiveResource objs - All classes that inherit from * ActiveResource should have these fields/values (unless overridden) */ public static class Fields extends ActiveField { public static final String CREATED_AT = "node:created"; 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 String LAST_MODIFIED_AT =“node:lastModified”; } }

public static final Fields Fields = new Fields();

    ... other model specific stuff ...

} }

I can then make a class Foo which extends my ActiveResource class 然后我可以创建一个扩展我的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 ...

Now, I can do the following: 现在,我可以做到以下几点:

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.

I can also access the Fields as a static field off my Model objects 我还可以从我的Model对象中访问Fields作为静态字段

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

So far this looks like a pretty nice solution, that will require minimal instruction for building out new Models. 到目前为止,这看起来是一个非常好的解决方案,需要最少的指令来构建新的模型。

I've just tried out some code trying to do what you've just described and it was really cumbersome. 我刚尝试了一些代码试图做你刚刚描述的内容而且它真的很麻烦。

If you have a Fields static inner class somewhere in a model class like this: 如果你在模型类中的某个地方有一个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);
    }
  }
}

and you extend this class like this: 你像这样扩展这个类:

public class ExtendedModel extends Model {

  public static class ExtendedFields extend Model.Fields {

    public static final String DOG = "dog";

    static {
      KEYS.add(DOG);
    }

    protected ExtendedFields() {}
  }
}

then its just wrong. 那是对的错。 If you call Model.Fields.getKeys() you'd get what you expect: [cat] , but if you call ExtendedModel.ExtendedFields.getKeys() you'd get the same: [cat] , no dog . 如果你调用Model.Fields.getKeys()你会得到你所期望的: [cat] ,但如果你调用ExtendedModel.ExtendedFields.getKeys()你会得到相同的: [cat] ,没有dog The reason: getKeys() is a static member of Model.Fields calling ExtendedModel.ExtendedFields.getKeys() is wrong because you really call Model.Fields.getKeys() there. 原因: getKeys()Model.Fields的静态成员,调用ExtendedModel.ExtendedFields.getKeys()错误的,因为你真的在那里调用Model.Fields.getKeys()

So you either operate with instance methods or create a static getKeys() method in all of your Fields subclasses, which is so wrong I can't even describe. 因此,您要么使用实例方法操作,要么在所有Fields子类中创建静态getKeys()方法,这是我甚至无法描述的错误。


Maybe you can create a Field interface which your clients can implement and plug into your model(s). 也许您可以创建一个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);
  } 
}

I wonder whether you really need a generated enumeration of fields. 我想知道你是否真的需要生成的字段枚举。 If you are going to generate a enum of a list the fields based on a model, why not generate a class which lists all the fields and their types? 如果要根据模型生成列表的enum ,为什么不生成列出所有字段及其类型的类? ie its not much harder to generate classes than staticly or dynamically generated enums and it much more efficient, flexible, and compiler friendly. 即,它比静态或动态生成的枚举更难生成类,它更高效,更灵活,编译友好。

So you could generate from a model something like 所以你可以从模型中生成类似的东西

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

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

Is there a real benefit to inventing your own type system? 发明自己的类型系统真的有好处吗?

Curses - For some reason my very lengthy response with the solution i came up with did not post. 诅咒 - 出于某种原因,我对我提出的解决方案的非常冗长的反应没有发布。

I will just give a cursory overview and if anyone wants more detail I can re-post when I have more time/patience. 我将简要概述一下,如果有人想要更多细节,我可以在我有更多时间/耐心时重新发布。

I made a java class (called ActiveField) from which all the inner Fields inherit. 我创建了一个java类(称为ActiveField),所有内部Fields都继承该类。 Each of the inner field classes have a series of fields defined: 每个内部字段类都定义了一系列字段:

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

In the ActiveRecord class i have a non-static method getKeys() which uses reflection to look at the all the fields on this , iterates through, gets their values and returns them as a List. 在ActiveRecord类我有一个非静态方法getKeys() ,它使用反射来看看在所有字段this ,通过迭代,得到他们的价值和将它们作为一个列表。

It seems to be working quite well - let me know if you are interested in more complete code samples. 它似乎工作得很好 - 如果您对更完整的代码示例感兴趣,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM