0

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;

}
macemers
  • 2,194
  • 6
  • 38
  • 55
  • 1
    Why not just "change" the values during runtime...? – TheLostMind Jun 04 '14 at 10:10
  • By using object of that class you can populate data at anytime – Cyclops Jun 04 '14 at 10:11
  • yes change the value of field according to array length or value. – Rishi Dwivedi Jun 04 '14 at 10:11
  • 2
    Have you considered defining a constructor? as I think that is what you are asking for... – anirudh Jun 04 '14 at 10:12
  • 2
    pass the array in the constructor and initialize the fields to the respective values inside the constructor – Infinite Recursion Jun 04 '14 at 10:13
  • Do i understand you correctly, you want some for loop which automagically sets the correct fields? Why aren't you using `String[] field; String[] name;`? – Absurd-Mind Jun 04 '14 at 10:21
  • @Absurd-Mind you're right. If loop `field`, how to call the `setField1` and `setField2`accordingly? – macemers Jun 04 '14 at 10:24
  • @user838204 you can achieve this with reflection, but in my opinion it is slow, error prone, hard to read, etc. Also your need of this may indicate that you may have a bad class design. – Absurd-Mind Jun 04 '14 at 10:29
  • @user838204 Couldn't you just pass an ```ArrayList``` of fields and names into your ```TestBean```? Otherwise you'll end up with an unruly amount of private instance variables all with a similar name. – David Yee Jun 04 '14 at 10:30
  • No, reflection is not usable here since fields have not the same root name (field1, field2,..., name1, name2...) and because `getDeclaratedFields` return an unsorted array of fields. – lpratlong Jun 04 '14 at 12:52
  • @CanadianDavid David, I like to find a way to loop and set the data because the fields end with 1,2,3,4...But it seems not possible – macemers Jun 04 '14 at 15:15
  • @user838204 You can use an array such as a ```HashMap``` where a key is associated with a value. If order is important, use a ```LinkedHashMap``` – David Yee Jun 04 '14 at 16:49
  • @CanadianDavid please revise your answer below to show me what your solution is because I don't understand how HashMap will help. – macemers Jun 05 '14 at 09:09

4 Answers4

5

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
Absurd-Mind
  • 7,884
  • 5
  • 35
  • 47
2

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.

lpratlong
  • 1,421
  • 9
  • 17
1

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.

David Yee
  • 3,515
  • 25
  • 45
0

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);
    }
} 
Absurd-Mind
  • 7,884
  • 5
  • 35
  • 47