macemers
macemers

Reputation: 2222

How to dynamically populate the fields of a class in Java

I know the following is not a good design but it's what I need to resolve

public final class TestBean {

    private String field1;
    private String field2;
    private String field3;

    public String getField1() {
        return field1;
    }
    public void setField1(String field1) {
        this.field1 = field1;
    }
    public String getField2() {
        return field2;
    }
    public void setField2(String field2) {
        this.field2 = field2;
    }
    public String getField3() {
        return field3;
    }
    public void setField3(String field3) {
        this.field3 = field3;
    }
}

And the fields in the class need to be populated dynamically.

Let say I have a array {"abc","def"}, and the class should initiated with field1="abc", field2="def" and field3=""; if the array is {"a"} and field1="a",field2="",field3="".

Is it possible to achieve that?


Updated: apparently I'm not stating the question well. In reality, the field is not just three, it's from field 1, field 2 to field 15. And then it's not just one field, there is another field call let say name, from name 1 to name 15:

public final class TestBean {

    private String field1;
    private String field2;
    ...
    private String field15;

    private String name1;
    private String name2;
    ...
    private String name15;

}

Upvotes: 0

Views: 4117

Answers (4)

Absurd-Mind
Absurd-Mind

Reputation: 7994

Another Answer to your Question:

What you want to achieve smells like a code smell. You have a list of String fields (with numbers!) which you want to initialize in order. This screams for arrays or collections.

example:

class TestBean {
    String[] field; // String field1; becomes field[0];
    String[] name;
}

If you really insist on using reflection to initialize your bean you could use the following code, but i highly recommend not to do so. You should instead refactor your classes and us a more appropriate Design:

public class TestBean {
    // initialize the field with the default value
    private String field1 = "";
    private String field2 = "";

    private String name1 = "";

    // establich an order on the fields, because getFields does not
    private static final List<String> order = Arrays.asList("field1", "field2", "name1");

    // using var args like in my other answer
    public TestBean(String... args) {
        // declared fields, instead of getFields so we get also private fields
        for (Field f : getClass().getDeclaredFields()) {

            // what is the position of the field
            int index = order.indexOf(f.getName());

            // if we found the field and a value is given set it.
            if (index < args.length && index >= 0) {
                try {
                    f.set(this, args[index]);                 
                } catch (IllegalArgumentException e) {
                    // should not happen
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // should not happen
                    e.printStackTrace();
                }
            } // you could use an else clause to set the field default
        }
    }

    public static void main(String[] args) {
        TestBean tb = new TestBean("abc", "foo");
        System.out.println(tb.field1);
        System.out.println(tb.field2);
        System.out.println(tb.name1);
    }
} 

Upvotes: 0

lpratlong
lpratlong

Reputation: 1461

You can do this, even if I don't like it (build an object according to String[] values is not very clean).

public final class TestBean {

    private String field1 = "";
    private String field2 = "";
    private String field3 = "";

    public TestBean(String[] values) {
        switch (values.length) {
            case 1:
                  field1 = values[0];
                  break;
            case 2:
                  field1 = values[0];
                  field2 = values[1];
                  break;
            case 3:
                  field1 = values[0];
                  field2 = values[1];
                  field3 = values[2];
                  break;
            default:
                  break;
        }
    }

    public String getField1() {
        return field1;
    }
    public void setField1(String field1) {
        this.field1 = field1;
    }
    public String getField2() {
        return field2;
    }
    public void setField2(String field2) {
        this.field2 = field2;
    }
    public String getField3() {
        return field3;
    }
    public void setField3(String field3) {
        this.field3 = field3;
    }
}

Edit

As your edition said, you need to populate fields with different names. The best way to do that is to use the Absurd-Mind's solution. You will not be able to use reflexion since the method #Class.getDeclaredFields() returns an unsorted array. CF javadoc for getDeclaredFields():

The elements in the array returned are not sorted and are not in any particular order.

If this array contained fields in the order they are written in the class, it would be possible to get it and populate fields with reflexion as you iterate over your String[]. But it's not possible here.

Upvotes: 2

Absurd-Mind
Absurd-Mind

Reputation: 7994

You can use varargs to achieve this:

public TestBean(String... args) {
    field1 = "";
    field2 = "";
    field3 = "";

    if (args.length >= 1) field1 = args[0];
    if (args.length >= 2) field2 = args[1];
    if (args.length >= 3) field3 = args[2];
}

and initialize your object this way:

new TestBean("abc", "foo");

String[] array = new String[]{"abc", "foo"};
new TestBean(array); // or this way if the array already exists

Upvotes: 5

David Yee
David Yee

Reputation: 3646

You should modify the TestBean class so that there are three constructors as such:

public final class TestBean {

    private String field1;
    private String field2;
    private String field3;

    public TestBean() {
        this("", "", "");
    }

    public TestBean(String field1) {
        this(field1, "", "");
    }

    public TestBean(String field1, String field2) {
        this(field1, field2, "");
    }

    public TestBean(String field1, String field2, String field3) {
        this.field1 = field1;
        this.field2 = field2;
        this.field3 = field3;
    }

    public String getField1() {
        return field1;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public String getField2() {
        return field2;
    }

    public void setField2(String field2) {
        this.field2 = field2;
    }

    public String getField3() {
        return field3;
    }

    public void setField3(String field3) {
        this.field3 = field3;
    }
}

Therefore, even if you only populate the first and/or the second fields, the rest of the fields will also be populated with a default blank string.

For example, all of the following uses will work:

new TestBean();
new TestBean("abc");
new TestBean("abc", "def");
new TestBean("abc", "def", "ghi");

Any remaining fields that were not filled into the constructors will be initialized as an empty string.

Upvotes: 1

Related Questions