Named Parameters in Java
I recently came across some code that looked like this:
o.doSomething1(
"Alfred E. Neumann",
"http://blog.schauderhaft.de",
42,
"c:\\temp\\x.txt",
23);
Circumstances were actually worse because there were about 20 or 30 such calls like that. I found not a single hint to the difference in meaning of the various literals.
Why is this so bad? The problem here is that a user of the method doSomething1 might accidently call it with:
o.doSomething1(
"Alfred E. Neumann",
"c:\\temp\\x.txt",
42,
"http://blog.schauderhaft.de",
23);
We can't tell if something went wrong. Compilation will occur. If you're lucky, it will blow up once the call executes. Maybe the application will behave oddly, for instance.
Some languages have a feature called named parameters that allows a syntax like this:
o.doSomething(
name = "Alfred E. Neumann",
link = "http://blog.schauderhaft.de",
ultimateAnswer = 42,
tempFile = "c:\\temp\\x.txt",
zip = 23);
Unfortunately, Java is not among these languages. Very often we can trick Java into actually helping us if we are willing to invest in some extra typing.
How about this?
o.doSomething2(
name("Alfred E. Neumann"),
link("http://blog.schauderhaft.de"),
ultimateAnswer(42),
tempFile("c:\\temp\\x.txt"),
zip(23));
It works like this: For every parameter there is a little class:
public class Name {
public final String name;
public Name(String n) {
name = n;
}
}
And a static method:
static Name name(String n) {
return new Name(n);
}
While this is a lot of typing, it gives you some type safety and the equivalence of named parameters. It might also be the first step toward value objects in your code. After all, when you have these values wrapped, why would you unwrap them early? Implement hashcode and equals if you need to do that.
We do have alternative ways to get similar effects—we’ll look at them in another blog post.(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Mihai Dinca - P... replied on Thu, 2012/08/09 - 2:01am
It seems too much to create so many classes for every parameter.
Better to have a single Immutable object with all needed properties and a fluent api to call like:
MyObject obj = MyObject.getInstance().setName(name).setLink(link).....
o.dosomtehing(obj)
This way you know what you are setting. Your method will have the same signature even if you will add or remove properties from MyObject .
Michal Xorty replied on Thu, 2012/08/09 - 2:32am
... or simply use Builder design pattern and give to methods fluent name (no need for trillion classes)
o.doSomething(new Something.Builder().name("Mike").path("C:/bla").zip(23).build());
Joachim Von Hacht replied on Thu, 2012/08/09 - 2:53am
Mike P(Okidoky) replied on Thu, 2012/08/09 - 10:50am
I'd like to propose another solution (well, aside from modifying the Java language, which I think it should be, because this is a rather fequent problem):
o.doSomething("name", "Mike", "path", "C:/bla", "zip", 23);
void doSomething(Object... objs)
{
Args args = new Args(objs);
String name = args.get("name", String.class);
String path = args.string("path");
int zip = args.number("zip");
...
}
Args' implementation is obvious.
Another improvement might be this: since map lookups aren't free, we might as well spend the cycles elsewhere:
import static Args.*;
void doSomethign(Object... args)
{
String name = stringArg(args, "name");
int zip = intArg(args, "zip');
}
stringArg, etc, are static methods in the Arg class. I think this might *actually* be cheaper than the former method so long as the arg count isn't too high (I'm being very picky here).
Also, I *think* that the java.lang.String method's equals method (and compareTo?) method, is missing an optimization. It should first compare the hash value before walking through all the characters. The hash value is cached after first calculation inside the string class. Look at the implementation of the java.lang.String class. It's missing this:
if (anotherString.hashCode() != hashCode())
return false;
One could get even more fancy. If the hash code hasn't been calculated yet, it could do both a comparison and build the hash code at the same time.
I see another optimization is missing in the String calss. the String(String original) constructor. It should be copying the hashCode. That way, if the original one already calculated it, the new String won't have to do it again.
Ahum, has any at original Sun properly scrutinized this class? Makes me wonder about all the other classes. I could imagine in a J2EE environment with countless lookups and processing, there might be a few percent to gain... These things are also not things that can be optimized automatically by the JVM. One should code with a live dis-assembler plugin in the IDE, and measure instant results from tweaking the code...
Dale Wyttenbach replied on Thu, 2012/08/09 - 1:12pm
Actually, you can do that in Java, or at least make it look like named parameters:
Leandro de Oliveira replied on Thu, 2012/08/09 - 6:13pm
in response to:
Michal Xorty
Jonathan Fisher replied on Thu, 2012/08/09 - 11:00pm
Honestly, if you actually need to do this, you must be dealing with an extrodinarily awful API design. Read Effective Java and/or watch this talk http://www.infoq.com/presentations/effective-api-design
The likely reason Java _doesn't_ have named parameters is performance... I can't imagine the impact such a language
bug'feature' would cause. (Checking, then resorting parameters on call stack frames? Yah fun).Writing better APIs would nullify this 'need', and if your domain is too complex, fluent APIs are a great alternative.
Bob Jones replied on Fri, 2012/08/10 - 8:30am
Andreas Haufler replied on Tue, 2012/08/14 - 2:33am
in response to:
Jonathan Fisher
Jonathan,
I agree with your and all the other opinions on using the builder pattern which is definitely the java way to go. However, I think that this _could_ be realized without any performance penalties. One could change the .class file structure to actually contain the parameter names. Then the compile could figure out all parameters at compile time, sort them in the expected order an fill non filled parameters with null. This might be handy in some cases, but on the other hand it would make the Java language even more complex...
What I actually would change immediatelly is the default pattern for generating setters in eclipse and other IDEs:
Instead of:
class Foo { private String foo; public void setFoo(String s) {...} }Generate:
class Foo { private String foo; public Foo setFoo(String s) {...; return this } }This way you'd get fluent APIs almost for free...Jonathan Fisher replied on Tue, 2012/08/14 - 10:01am
in response to:
Andreas Haufler
Andreas, that is brilliant!
You know, there is another solution... make sure you have appropriate JavaDoc @Param tags and use a sane IDE. A quick hover over the method in Eclipse highlights pops up the JavaDoc.