开发者

Dynamically return a list of all class variable values in Java

I am creating a helper class in parsing XML elements, so the developer do not need to know the exact name and capitalization of the XML fields.

private static class TagNames{
      public static String RESOURCE_ID = "ResourceId";
      public static String RESOURCE_NAME = "ResourceName";
      public开发者_开发知识库 static String RESOURCE_PRICE = "ResourcePrice";
}

This makes it easier to do things like:

someXMLParser.getValueByTagName(TagNames.RESOURCE_ID);

My question is this. If I want to iterate over all the fields declared in class TagNames, how do I do that? Pseudocode:

For tag in TagNames:
   someXMLParser.getValueByTagName(tag)

I know I will probably have to restructure all of this. But I can't figure out a way to make the names easily accessible as well as iterable, without any duplication.

Any suggestions?


You're literally asking for a solution based on reflection, but I think a Java Enum may be a better choice in this case. Building on Frederick's example:

public class EnumTest {
    public enum Tags {
        RESOURCE_ID("ResourceId"), 
        REOURCE_NAME("ResourceName"), 
        RESOURCE_PRICE("ResourcePrice");

        private final String tagName;
        Tags(String tagName) {
            this.tagName = tagName;
        }

        public String getTagName() {
            return tagName;
        }
    }

    public static void main(String[] args) {
        for(Tags tag : Tags.values()) {
            System.out.println("const:" + tag.name() 
                    + " tagName:" + tag.getTagName());
        }
        // API user might do e.g.:
        // document.getValueForTag(Tags.REOURCE_NAME);
    }
}


Although I agree that you should probably use enums or ResourceBundles, here's a solution to your actual question. A method that generates a Map name -> value from all public constants in a given class (the only thing that's missing should be try / catch or throws)

public static Map<String, Object> getConstantValues(Class<?> clazz){

    Map<String, Object> constantValues = new LinkedHashMap<String, Object>();
    for(Field field : clazz.getDeclaredFields()){
        int modifiers = field.getModifiers();
        if(Modifiers.isPublic(mod)
            && Modifiers.isStatic(mod) && Modifiers.isFinal(mod)){
            constantValues.put(field.getName(), field.get(null));
        }
    }
    return constantValues;
}


You may want to consider using a ResourceBundle instead of a class to store the tag names. May require a little bit of reworking of your code but it will be easier to produce a list of tags compared to what you are doing now, and adding a new tag won't require much work other then adding a line to the properties file.


You can do this quite easily using enum and an accompanying array:

public class Main {

    public enum TagName { RESOURCE_ID, REOURCE_NAME, RESOURCE_PRICE }

    private static String[] tags = {"ResourceID", "ResourceName", "ResourcePrice"};

    public static String getValueByTagName(TagName tag) {

    return tags[tag.ordinal()];
    }

    public static void main(String[] args) {

    System.out.println("Calling by getValueByTagName:");
    System.out.println(getValueByTagName(TagName.RESOURCE_ID));

    System.out.println("Calling TagName.values() for loop:");

    for (TagName t : TagName.values()) {

        System.out.println(getValueByTagName(t));
    }
    }
}


Using an enum is a good fit, especially if you use a custom constructor and the built in "values" method:

public class Main {
  public static enum TagName {
    RESOURCE_ID("ResourceId"),
    RESOURCE_NAME("ResourceName"),
    RESOURCE_PRICE("ResourcePrice"),
    ;
    private String s;
    private TagName(String s) { this.s = s; }
    public String toString() { return this.s; }
    public static String[] strings() {
      List<String> ss = new ArrayList<String>();
      for (TagName tagName : TagName.values()) {
        ss.add(tagName.toString());
      }
      return ss.toArray(new String[ss.size()]);
    }
  }
  public static void main(String[] args) {
    // Use TagName.values() for the enums, or for strings...
    for (String s : TagName.strings()) {
      System.out.println(s);
    }
  }
}

This way you can simply add new tags and they'll automatically get picked up by the "strings" method; for extra performance you could compute that string array just once, statically, since you can't change the set of enums dynamically. You could get even fancier by auto-generating the tag strings from their constant values, if they are really normalized...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜