Trevor Dixon
Trevor Dixon

Reputation: 24342

Java—how can I dynamically reference an object's property?

In javascript, I can do this:

function MyObject(obj) {
    for (var property in obj) {
        this[property] = obj[property];
    }
}

Can I do anything close in Java?

class MyObject {
    String myProperty;

    public MyObject(HashMap<String, String> props) {
        // for each key in props where the key is also the name of
        // a property in MyObject, can I assign the value to this.[key]?
    }
}

Upvotes: 10

Views: 17639

Answers (4)

Alexander Mills
Alexander Mills

Reputation: 99960

First, I would use a map if at all possible:

class MyObject {

     // String myProperty; // ! not this
     HashMap<String,String> myProperties;  // use this instead

}

but let's say you wanted to set the fields dynamically.

public MyObject(HashMap<String, String> props) {
    for (Map.Entry<String,String> entry : props.entrySet()) {
        Field field = this.getClass().getField(entry.getKey());
        field.set(this, entry.getValue());
    }
}

of course, you will want to use a try/catch in the above constructor.

Upvotes: 4

Puce
Puce

Reputation: 38122

Well, if you really want to go down the reflection raod, then I suggest to have a look at the Introspector class and get the list of PropertyDescriptors from the BeanInfo.

Upvotes: 2

Joel Westberg
Joel Westberg

Reputation: 2736

Yes, you can do it by reflection with something along the following lines:

/**
 * Returns a list of all Fields in this object, including inherited fields.
 */
private List<Field> getFields() {
    List<Field> list = new ArrayList<Field>();
    getFields(list, getClass());
    return list;
}

/**
 * Adds the fields of the provided class to the List of Fields. 
 * Recursively adds Fields also from super classes.
 */
private List<Field> getFields(List<Field> list, Class<?> startClass) {
    for (Field field : startClass.getDeclaredFields()) {
        list.add(field);
    }
    Class<?> superClass = startClass.getSuperclass();
    if(!superClass.equals(Object.class)) {
        getFields(list, superClass);
    }
}

public void setParameters(Map<String, String> props) throws IllegalArgumentException, IllegalAccessException {
    for(Field field : getFields()) {
        if (props.containsKey(field.getName())) {
            boolean prevAccessible = field.isAccessible();
            if (!prevAccessible) {
                /*
                 * You're not allowed to modify this field. 
                 * So first, you modify it to make it modifiable.
                 */
                field.setAccessible(true);
            }
            field.set(this, props.get(field.getName()));

            /* Restore the mess you made */
            field.setAccessible(prevAccessible);
        }
    }
}

However, if you are not very familiar with Java, this approach should be avoided if at all possible, as it is somewhat dangerous and error prone. For instance, there is no guarantee that the Field you are attempting to set are actually expecting a String. If it is the case that they are not, your program will crash and burn.

Upvotes: 4

Jacob Schoen
Jacob Schoen

Reputation: 14192

Not that I disagree with Joel's answer, but I do not think it is not quite that difficult, if you essentially just want a best effort. Essentially check if it is there, and if it is try to set. If it works great if not, oh well we tried. For example:

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class MyObject {

    protected String lorem;
    protected String ipsum;
    protected int integer;


    public MyObject(Map<String, Object> valueMap){
        for (String key : valueMap.keySet()){
            setField(key, valueMap.get(key));
        }
    }

    private void setField(String fieldName, Object value) {
        Field field;
        try {
            field = getClass().getDeclaredField(fieldName);
            field.set(this, value);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Map<String, Object> valueMap = new HashMap<String, Object>();
        valueMap.put("lorem", "lorem Value");
        valueMap.put("ipsum", "ipsum Value");
        valueMap.put("integer", 100);
        valueMap.put("notThere", "Nope");

        MyObject f = new MyObject(valueMap);
        System.out.println("lorem => '"+f.lorem+"'");
        System.out.println("ipsum => '"+f.ipsum+"'");
        System.out.println("integer => '"+f.integer+"'");
    }
}

Upvotes: 8

Related Questions