Jens Schauder is software developer since 1997. He loves software development for the constant challenges and constantly changing environment. A great chance to learn and teach. He is also blogger, author of various articles and speaker at conferences. Jens is a DZone MVB and is not an employee of DZone and has posted 90 posts at DZone. You can read more from them at their website. View Full User Profile

Named Parameters in Java

08.09.2012
| 18405 views |
  • submit to reddit

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.
Published at DZone with permission of Jens Schauder, author and DZone MVB. (source)

(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

Refactor all methods to take objects as parameters, not primitive parts of some object (probably a bad OO-model in background)

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:    

Long number;
List<Long> factors = getFactors(number = 10L); 
 

Leandro de Oliveira replied on Thu, 2012/08/09 - 6:13pm in response to: Michal Xorty

You can even use my builder implementation generator to avoid writing too much code: http://code.google.com/p/fastbuilder/

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

Why not pass in a Map, like most jQuery plugins? Sure you lose type safety, but at least you know that you're setting arg X = string1 and arg Y = string2. I do wish Java had a more concise Map syntax... *Helloooo Groovy :)

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.

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.