I was doing some Java programming today, and I noticed an anti-pattern that was in an existing code base. It went something like the following:
public Class Klass {
...
public Klass(int foo, boolean flag, String s1, String s2, String s3, String s4) {
initialize(foo, flag, s1, s2, s3, s4);
}
public Klass(int foo, boolean flag, String s1, String s2, String s3) {
initialize(foo, flag, s1, s2, s3, null);
}
public Klass(int foo, boolean flag, String s1, String s2) {
initialize(foo, flag, s1, s2, null, null);
}
public Klass(int foo, boolean flag, String s1) {
initialize(foo, flag, s1, null, null, null);
}
public Klass(int foo, boolean flag) {
initialize(foo, flag, null, null, null, null);
}
private initialize(int foo, boolean flag, String s1, String s2, String s3, String s4) {
_foo = foo;
_flag = flag;
_s1 = s1;
_s2 = s2;
_s3 = s3;
_s4 = s4;
}
...
}
Immediately, my Don’t Repeat Yourself sensor went off. It felt like the previous coder was essentially trying to use some default values for a method. You can do something like this in other languages, even other statically typed ones. In Ruby, it would be pretty simple:
class klass
...
def initialize(foo, flag, s1 = nil, s2 = nil, s3 = nil, s4 = nil)
...
end
...
end
However, I looked online and saw several instances of people saying “you can’t do that in Java.” So how else can we reduce this duplication?
One way that was suggested was to make better use of the object itself:
public Class Klass {
...
public Klass(int foo, boolean flag, String s1, String s2, String s3, String s4) {
_foo = foo;
_flag = flag;
_s1 = s1;
_s2 = s2;
_s3 = s3;
_s4 = s4;
}
public Klass(int foo, boolean flag, String s1, String s2, String s3) {
this(foo, flag, s1, s2, s3, null);
}
public Klass(int foo, boolean flag, String s1, String s2) {
this(foo, flag, s1, s2, null, null);
}
public Klass(int foo, boolean flag, String s1) {
this(foo, flag, s1, null, null, null);
}
public Klass(int foo, boolean flag) {
this(foo, flag, null, null, null, null);
}
...
}
This at least takes away the initialization method. I suppose you could make it shorter by having each constructor call the one immediately prior. You would remove a few nulls here and there. However, there is a deeper problem here, which was demonstrated in the code I was actually working with. What if we wanted to add even more strings? Why not just store the strings in an array? But if we have one string, do we need to store it in an array?
Ruby has a solution for this problem–the splat (*) operator:
def initialize(foo, flag, *rest)
foo = foo
flag = flag
strings = *rest
end
If the rest
parameter is nil, then strings
will be nil. If the rest
parameter is just a single value, like “hello”, then strings
will be [“hello”]. It follows that if multiple values are passed in, then strings
will contain an array of those values.
My first instinct was that Java did not have a construct that would allow us to do the same thing. However, a search revealed that there was an operator in Java that would allow us to do something like Ruby’s args or splat. Enter the ellipsis (…) feature, first released in Java 1.5:
public Class Klass {
...
public Klass(int foo, boolean flag, String... strings) {
_foo = foo;
_flag = flag;
_strings = strings;
}
...
}
Ahh, that makes me happy. We went from about twenty lines to five with a single trick. Now, if there is some reason that only four strings can be allowed, we can do some additional parameter checking, but it seems like this is considerably more robust already. Like in Ruby, the varargs feature can only be used at the end of the method signature.
There were other parts of the codebase that had something like:
public Class Klass {
...
public Klass(String string1, Shape shape1, String string2, Shape shape2, String string3, Shape shape3) {
...
}
...
}
Where each String refers somehow to the associated Shape. In this case, you could just use a HashMap with generics, as in HashMap<String, Shape>
.
You probably already knew all of this, but I had never used the varargs feature before in Java. Hope this helped.