tags:

views:

673

answers:

5

How to implement Named Parameter idiom in Java? (especially for constructors)

I am looking for an Objective-C like syntax and not like the one used in JavaBeans.

A small code example would be fine.

Thanks.

+15  A: 

The best Java idiom I've seem for simulating keyword arguments in constructors is the Builder pattern, described in Effective Java 2nd Edition.

The basic idea is to have a Builder class that has setters (but usually not getters) for the different constructor parameters. There's also a build() method. The Builder class is often a (static) nested class of the class that it's used to build. The outer class's constructor is often private.

The end result looks something like:

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setSize(int size) {
      this.size = size;
      return this;
    }

    public Builder setColor(Color color) {
      this.color = color;
      return this;
    }

    public Builder setName(String name) {
      this.name = name;
      return this;
    }

    // you can set defaults for these here
    private int size;
    private Color color;
    private String name;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    size = builder.size;
    color = builder.color;
    name = builder.name;
  }

  private final int size;
  private final Color color;
  private final String name;

  // The rest of Foo goes here...
}

To create an instance of Foo you then write something like:

Foo foo = Foo.builder()
    .setColor(red)
    .setName("Fred")
    .setSize(42)
    .build();

The main caveats are:

  1. Setting up the pattern is pretty verbose (as you can see). Probably not worth it except for classes you plan on instantiating in many places.
  2. There's no compile-time checking that all of the parameters have been specified exactly once. You can add runtime checks, or you can use this only for optional parameters and make required parameters normal parameters to either Foo or the Builder's constructor. (People generally don't worry about the case where the same parameter is being set multiple times.)

You may also want to check out this blog post (not by me).

Laurence Gonsalves
That really isn't named parameters in the way Objective-C does them. That looks more like a fluent interface. It's really not the same thing.
Asaph
I like using `.withFoo`, rather than `.setFoo`: `newBuilder().withSize(1).withName(1).build()` rather than `newBuilder().setSize(1).setName(1).build()`
notnoop
Asaph: yes, I know. Java doesn't have named parameters. That's why I said this is "the best Java idiom I've seem for *simulating* keyword arguments". Objective-C's "named parameters" are also less than ideal, since they force a particular ordering. They aren't true keyword arguments like in Lisp or Python. At least with the Java Builder pattern you just need to remember the names, not the order, just like real keyword arguments.
Laurence Gonsalves
notnoop: I prefer "set" because these are setters that mutate the state of the Builder. Yes, "with" looks nice in the simple case where you're chaining together everything, but in more complicated cases where you've got the Builder in its own variable (perhaps because you're conditionally setting properties) I like that the set prefix makes it completely clear that the Builder is being mutated when these methods are called. The "with" prefix sounds functional to me, and these methods are decidedly not functional.
Laurence Gonsalves
`There's no compile-time checking that all of the parameters have been specified exactly once.` This problem can be overcome by returning interfaces `Builder1` to `BuilderN` where each covers either one of the setters or the `build()`. It's much more verbose to code, but it comes with compiler support for your DSL and makes auto-completion very nice to work with.
rsp
rsp: Good point. I've occasionally considered using n-Builder classes like you describe, but have never actually used this approach because the extra verbosity and the loss of the ability to reorder parameters always seemed like too high a price to pay. (You could get the reordering back by creating 2^n-Builder classes, but obviously that's far worse in the verbosity department for all but very tiny values of n.) I generally put my required parameters on either the Builder's constructor or build() method, and use setters for optional parameters.
Laurence Gonsalves
+4  A: 

Java does not support Objective-C-like named parameters for constructors or method arguments. Furthermore, this is really not the Java way of doing things. In java, the typical pattern is verbosely named classes and members. Classes and variables should be nouns and method named should be verbs. I suppose you could get creative and deviate from the Java naming conventions and emulate the Objective-C paradigm in a hacky way but this wouldn't be particularly appreciated by the average Java developer charged with maintaining your code. When working in any language, it behooves you to stick to the conventions of the language and community, especially when working on a team.

Asaph
Would the downvoter care to comment?
Asaph
+1 - for the advice about sticking to the idioms of the language you are currently using. Please think of the other people who will need to read your code!
Stephen C
+2  A: 

this is worth of mentioning

Foo foo = new Foo(){{ color=red;  name="Fred"; size=42; }};

the so called "double brace initializer". actually an anonymous class with instance initializer.

irreputable
Beware of reformatting the above code in Eclipse.
Thorbjørn Ravn Andersen
Interesting technique but seems a little expensive as it will create a new class every time I use it in my code.
Red Hyena
+3  A: 

You could use a usual constructor and static methods that give the arguments a name:

public class Something {

    String name;
    int size; 
    float weight;

    public Something(String name, int size, float weight) {
        this.name = name;
        this.size = size;
        this.weight = weight;
    }

    public static String name(String name) { 
        return name; 
    }

    public static int size(int size) {
        return size;
    }

    public float weight(float weight) {
        return weight;
    }

}

Usage:

import static Something.*;

Something s = new Something(name("pen"), size(20), weight(8.2));

Limitations compared to real named parameters:

  • argument order is relevant
  • variable argument lists are not possible with a single constructor
  • you need a method for every argument
  • not really better than a comment (new Something(/*name*/ "pen", /*size*/ 20, /*weight*/ 8.2))

If you have the choice look at Scala 2.8. http://www.scala-lang.org/node/2075

deamon
One downside to this approach is you *must* get the arguments in the correct order. The above code would let you write: Something s = new Something(name("pen"), size(20), size(21));Also, this approach does not help you avoid typing in optional arguments.
Matt Quail
Yes, I already mentioned this downsides.
deamon
+3  A: 

Here's a little variation of the technique given in Joshua Bloch's Effective Java. Here I have made an attempt to make the client code more readable (or perhaps more DSLish).

/**
 * Actual class for which we want to implement a 
 * named-parameter pseudo-constructor
 */
class Window{
    protected int x, y, width, height;
    protected boolean isResizable;
    protected String title;

    public void show(){
        // Show the window
        System.out.printf("Window \"%s\" set visible.%n",title);
    }

    /**
     * This class is only used to set the parameter values
     */
    static class HavingProperties extends Window{

        public HavingProperties x(int value){
            this.x=value;
            return this;
        }

        public HavingProperties y(int value){
            this.y=value;
            return this;
        }

        public HavingProperties width(int value){
            this.width=value;
            return this;
        }

        public HavingProperties height(int value){
            this.height=value;
            return this;
        }

        public HavingProperties resizable(boolean value){
            this.isResizable=value;
            return this;
        }

        public HavingProperties title(String value){
            this.title=value;
            return this;
        }
    }
}

public class NamedParameterIdiomInAction {
    public static void main(String... args){
        Window window=new Window.HavingProperties().x(10).y(10).width(100).
                height(100).resizable(true).title("My App");
        window.show();
    }
}

Please note that with this variation, you can also give meaningful names to your pseudo-constructors.

missingfaktor
Doesn't look much better than a fluent interface to me: new FluentWindow.setX(10).setY(10).setWidth(100).setHeight(100).setResizable(true).setTitle("My App")
Software Monkey
@Software Monkey : I said **more DSLish**.
missingfaktor