Using JDK 5 varargs as bulk setter method

After thinking I couldn't see using the new varargs feature in JDK 5 every day, I thought of a potential use that could cut down on repetitive code: initializing a bean or value object.

As an example, say I wanted to create Person class that contains the person's first name, middle name, last name, and a nickname. I want all values to default to an empty string if not supplied by the caller.

For a standard Java bean, I could create a class with all the "name" instance variables initialized to the empty string, then write setters for the values. That way, Person bean instances would have the values set by callers, but contain empty strings for any values unset. For convenience, I'd probably include a bulk setter method that would take all four strings. Something like:
setAllNames(String last, String first, String middle, String nick) {
lastName = last;
firstName = first;
middleName = middle;
nickName = nick;
}
But maybe the caller would want to just set the first name and last name. Of course, they could call the method passing in empty parameters:
setAllNames("McQueeney", "Tom", "", "");
but for convenience -- especially if there were more the two unneeded parameters, I'd probably want to provide the method:
setLastFirst(String last, String first) {
lastName = last;
firstName = first;
}
And now I can see where having a method that would take a variable number of arguments could help.

As a test, I wrote the following sample Person class. Instead of providing all the setters and getters and creating a bulk setter, as mentioned above, I put all property-setting in the constructor for two reasons: first, to keep the code short, and second, to show that a varargs constructor becomes the default constructor. This seems like a neat little feature. (While in the back of my mind I'm thinking, "How are programmers going to abuse this neat feature?")

Here's my Person class, coded using JDK 5.
public class Person {
private String lastName;
private String firstName;
private String middleName;
private String nickName;
public Person(String... params) {
lastName = firstName = middleName = nickName = "";
if (params.length > 0) lastName = params[0];
if (params.length > 1) firstName = params[1];
if (params.length > 2) middleName = params[2];
if (params.length > 3) nickName = params[3];
if (params.length > 4) {
throw new IllegalArgumentException(
"Constructor called with too many arguments"
);
}
}
public String toString() {
return "last: '" + lastName + "', first: '" + firstName +
"', middle: '" + middleName + "', nick: '" + nickName + "'";
}
public static void main(String[] args) {
System.out.println(
"No args: " +
new Person()
);
System.out.println(
"1 arg: " +
new Person("McQueeney")
);
System.out.println(
"2 arg: " +
new Person("McQueeney", "Tom")
);
System.out.println(
"3 arg: " +
new Person("McQueeney", "Tom", "X")
);
System.out.println(
"4 arg: " +
new Person("McQueeney", "Tom", "X", "Spiff")
);
System.out.println(
"5 arg: " +
new Person("McQueeney", "Tom", "X", "Spiff", "bad!")
);
}
}
(I'm not recommending the above coding style as a best-practice!) Here's the output:
> java -cp . com.mcqueeney.tiger.Person
No args: last: '', first: '', middle: '', nick: ''
1 arg: last: 'McQueeney', first: '', middle: '', nick: ''
2 arg: last: 'McQueeney', first: 'Tom', middle: '', nick: ''
3 arg: last: 'McQueeney', first: 'Tom', middle: 'X', nick: ''
4 arg: last: 'McQueeney', first: 'Tom', middle: 'X', nick: 'Spiff'
Exception in thread "main" java.lang.IllegalArgumentException:
Constructor called with too many arguments
at com.mcqueeney.tiger.Person.(Person.java:19)
at com.mcqueeney.tiger.Person.main(Person.java:51)
The benefit of varargs now is I can provide a bulk property setter with only one method, and allow the caller to pick how many of the properties to set.

Of course, bulk setters like this suffer from the same problems as other bulk setters:
  • You can't name the parameters, so you have to read the javadocs to figure out what order to pass everything.
  • You still have to provide the missing values for properties in the "middle" of the varargs set that you really don't want to set.
(By the way, I think the Groovy language allows you to specify the parameter names when you call a method.)

I can see bulk setters using varargs reducing some code. For instance, the java.util.GregorianCalendar has constructors that takes initial values for the date and time:
public GregorianCalendar(int year, int month, int dayOfMonth);
public GregorianCalendar(int year, int month, int dayOfMonth,
int hourOfDay, int minute);
public GregorianCalendar(int year, int month, int dayOfMonth,
int hourOfDay, int minute, int second);
which seem like they could be implemented with one Integer varargs constructor, using the new auto-unboxing feature in JDK 5 to easily set the integer properties. (Out of curiosity, I checked the source code for JDK beta 3 Build 60, and, no, they didn't retrofit GregorianCalendar this way.)

When searching around the Internet for code samples using varargs, one of the more-complete explanations was this Chapter 5 excerpt from Java 1.5 Tiger: A Developer's Notebook, by Brett McLaughlin and David Flanagan.

4 thoughts on “Using JDK 5 varargs as bulk setter method

  1. I think you are very wrong… varargs should be used for System.out.printf(…), for X.query(“select * from Table where x=? and date< ?", 3, now), or X.setProperties(person, "firstName", "Tom", "lastName", "Mcq", "ssn", 123456789, ...).

    for you example, first of all I won’t have one method to set them all. If I do, I won’t use varargs, all params should be provided, even if null: setAllNames(“J”, “Yu”, null, null).

  2. I agree 101% with J Yu. I think varargs are well suited for things like printf etc. I cringe at the idea of “bulk setters” being implemented that way and more generally varags being used where traditional method overloading really should be done. That said, I bet we see a whole lot of that kind of code written anyway.
    With JDK 5, we have a whole new arsenal of new guns that we may use to shoot ourselves in our collective feet. Take care not to do that.

  3. Named and default arguments are wonderful (Oracle PL/SQL has them, and I wish Java did)
    FUNCTION setAllNames(
    first_name varchar2 := null,
    middle_initial varchar2 := null,
    last_name varchar2 := )
    IS
    BEGIN

    END;
    Invocation :
    setAllNames(first_name => ‘Jason’, last_name => ‘Vogel’);
    setAllNames(last_name => ‘Vogel’,first_name => ‘Jason’);
    This construct is clean, efficient and means that you have a lot less to overload. You don’t end up with
    setAllNames(string first_name, string last_name)
    setAllNames(string first_name, string middle_initial, string last_name)
    setAllNames(string first_name, string middle_initial, string last_name, string nick_name)
    You just have the last one with defaults… [of Null]
    Jason Vogel

  4. It’s funny that you say “How are programmers going to abuse this neat feature?”. Because I think that what you did is a great example of how programmers are going to abuse the feature! Hetrogeneous parameters (uh, parameters that are different) should be explicitly named as parameters. Otherwise, what do you have to go on to figure out what they are – the javadocs (you hope)? Have ever tried to read a constructor as it is now (new Something(true, 15, false, false), for example). It’s hard enough to read.

    I feel confident saying that varargs should only be used when all the variable arguments are the same “kind” of parameter. For example, the var args in printf are all the same “kind” of argument – they are all formatting information. Likewise, they could add a constructor to ArrayList that had a varargs parameter, so you could say new ArrayList(1, 2, 3, 4).

    But if your parameters are different “kinds” of parameters, it’s a nightmare to use varargs. Like new GregorianCalendar(1990, 5, 5, 5, 5, 5) isn’t already a nightmare to read!

Comments are closed.