104

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.

VishnuVS
  • 1,055
  • 2
  • 14
  • 29
Red Hyena
  • 2,988
  • 5
  • 25
  • 24

21 Answers21

122

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
  • 137,896
  • 35
  • 246
  • 299
  • 13
    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 Jan 01 '10 at 06:40
  • 33
    I like using `.withFoo`, rather than `.setFoo`: `newBuilder().withSize(1).withName(1).build()` rather than `newBuilder().setSize(1).setName(1).build()` – notnoop Jan 01 '10 at 06:46
  • 20
    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 Jan 01 '10 at 06:50
  • 16
    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 Jan 01 '10 at 06:58
  • 4
    `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 Jan 01 '10 at 21:42
  • 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 Jan 01 '10 at 22:51
  • @T.J.Crowder If you look at the example code in the answer you'll see that the `Foo` constructor takes a `FooBuilder` as its only parameter. Essentially, the builder *is* the options class. The constructor (and hence, the `build()` method) should fail if the builder is in an invalid state, so there is no danger of the constructor returning an object that is not properly constructed. – Laurence Gonsalves Jan 08 '14 at 23:58
  • @LaurenceGonsalves: %\*!@"! How did I miss that? Too much going on. I'd upvote the answer but I already did that. Thanks and apologies. That's what I was thinking it should be, but better (no surprise). I've deleted my mistaken comment, we should clean these up too. Thanks for being so kind in your reply above. – T.J. Crowder Jan 09 '14 at 08:27
  • Couldn't you achieve this same effect by having the setters return `this`? – Carcigenicate Jul 11 '15 at 20:28
  • 2
    @Carcigenicate With the builder pattern you can have the built objects be immutable. It also separates unchecked values (the builders) from values that have been checked for validity and consistency by giving them a different type. This means that once a non-builder object in constructed you can rely on it being valid and staying that way, and you can use static types to enforce this. With only setters it often means that you have to allow inconsistent states, and you can't rely on the type system as much. You end up having to do a lot more runtime checking and defensive copying. – Laurence Gonsalves Jul 13 '15 at 00:18
  • 2
    For those interested in the Builder pattern without writing a lot of boilerplate, Project Lombok has implemented it as an annotation (https://projectlombok.org/features/Builder.html). The usual caveats about adopting Lombok and their deep magic apply, of course. – Johannes Jander Feb 20 '17 at 09:40
  • 1
    @JohannesJander Good suggestion, though at this point I feel like if you want to use Lombok, why not just switch to Kotlin? I've been using Kotlin for my recent JVM projects, and while it is a different language, it's extremely interoperable with Java, and it's very easy to pick up. Instead of `Builder` pattern you can use actual named parameters, and there are many other syntactic niceties. – Laurence Gonsalves Feb 22 '17 at 22:12
  • @LaurenceGonsalves Oh, Kotlin is absolutely the better language - I am just not sure if it will see wide-spread adoption. In my current occupation (Big-Corp Enterprises), I have been fighting for 3 years to be able to build web frontends in Javascript, not GWT, so things like Kotlin are non-starters. And the question had a "Java" tag, so recommending a language-switch is a bit over the top, IMO. – Johannes Jander Feb 22 '17 at 22:22
86

This is worth of mentioning:

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

the so called double-brace initializer. It is actually an anonymous class with instance initializer.

irreputable
  • 44,725
  • 9
  • 65
  • 93
  • 32
    Interesting technique but seems a little expensive as it will create a new class every time I use it in my code. – Red Hyena Jan 01 '10 at 11:23
  • 9
    Auto-formatting, subclassing and serialization warnings aside, this is actually quite close to the C# syntax for property-based initialization. However, C# as of 4.0 also has named parameters, so programmers are really spoilt for choice, unlike Java programmers who have to simulate idioms that prevent them shooting themselves in the foot later. – Steve Dec 27 '12 at 01:49
  • 3
    Glad to see this is possible but I had to downvote as this solution is expensive as Red Hyena pointed out. Can't wait until Java actually supports named parameters like Python does. – Gattster Feb 12 '13 at 01:06
  • 18
    Upvote. This answers the question in the most readable, concise way. Fine. It's "not performant". How many extra milliseconds & bits are we talking here? Ones of them? Tens? Don't get me wrong -- I won't be using this as I'd hate to be executed by a verbose Java nut (pun intended ;) – steve May 02 '13 at 07:50
  • 4
    Perfect! Only requires public/protected fields. It's definitely the best solution, causing much less overhead than builder. Hyena/Gattster: please (1) read JLS and (2) check generated bytecode before writing your comments. – Jonatan Kaźmierczak Jan 24 '17 at 00:40
  • 2
    No, it is not perfect. If you use double-brace initializer inside Spring/JEE/CDI beans that `foo` will prevent that bean from being GCed and `foo` might no longer be Serializable,even if `Foo` was. – user158037 Feb 22 '18 at 12:02
  • 2
    The real problem is that this instance of Foo will keep a reference to the instance of the enclosing class where it is created. See [ReallyHeavyObject in this answer](https://stackoverflow.com/a/27521360/1143274) for an example. – Evgeni Sergeev Oct 12 '18 at 17:00
29

Java 8 style:

public class Person {
    String name;
    int age;

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    static PersonWaitingForName create() {
        return name -> age -> new Person(name, age);
    }

    static interface PersonWaitingForName {
        PersonWaitingForAge name(String name);
    }

    static interface PersonWaitingForAge {
        Person age(int age);
    }

    public static void main(String[] args) {

        Person charlotte = Person.create()
            .name("Charlotte")
            .age(25);

    }
}
  • named parameters
  • fix order of arguments
  • static check -> no nameless Person possible
  • hard to switch arguments of same type by accident (like it is possible in telescop constructors)
Alex
  • 947
  • 9
  • 13
  • 3
    nice. What a pitty it doesn't have variable argument order. (But not to say that I would use this ... ) – Scheintod Sep 16 '15 at 17:28
  • 1
    This is a brilliant idea. The definition of `create()` stopped me in my tracks. I have never seen that style of lambda chaining in Java. Did you first discover this idea in another language with lambdas? – kevinarpe Feb 25 '18 at 10:00
  • 3
    It's called currying: https://en.wikipedia.org/wiki/Currying. Btw: Maybe it is a clever idea, but I wouldn't recomend this named arguments style. I tested it in a real project with many arguments and it leads to hard to read and hard to navigate code. – Alex Apr 03 '18 at 11:19
  • 1
    Eventually, Java will be given Visual Basic style named parameters. Java did not before because C++ does not. But we will get there eventually. I would say that 90% of Java polymorphism is just hacking around optional parameters. – Tuntable Jul 22 '19 at 03:34
26

You could also try to follow advice from here.

int value;
int location;
boolean overwrite;
doIt(value=13, location=47, overwrite=true);

It's verbose on the call site, but overall gives the lowest overhead.

Michael Mior
  • 28,107
  • 9
  • 89
  • 113
rkj
  • 8,787
  • 2
  • 29
  • 35
  • 3
    Good cause the low overhead, but it feels so hackish. I'm gonna probably use the Builder() method for cases where there are many arguments. – Gattster Feb 12 '13 at 01:38
  • 32
    I think this completely misses the point of named parameters. (which is have *something associate names with values*). There is no indication *whatsoever* if you reverse the order. Instead of doing this I would advice of simply adding a comment: `doIt( /*value*/ 13, /*location*/ 47, /*overwrite*/ true )` – Scheintod Sep 16 '15 at 17:16
11

I would like to point out that this style addresses both the named parameter and the properties features without the get and set prefix which other language have. Its not conventional in Java realm but its simpler and shorter, especially if you have handled other languages.

class Person {
    String name;
    int age;

    // name property
    // getter
    public String name() { return name; }

    // setter
    public Person name(String val)  { 
        name = val;
        return this;
    }

    // age property
    // getter
    public int age() { return age; }

    // setter
    public Person age(int val) {
       age = val;
       return this;
    }

    public static void main(String[] args) {
  
        // addresses named parameter
        Person jacobi = new Person().name("Jacobi Adane").age(3);
  
        // addresses property style
        System.out.println(jacobi.name());
        System.out.println(jacobi.age());
  
        // updates property values
        jacobi.name("Lemuel Jacobi Adane");
        jacobi.age(4);

        System.out.println(jacobi.name());
        System.out.println(jacobi.age());
    }
}
LEMUEL ADANE
  • 8,336
  • 16
  • 58
  • 72
7

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.

pdem
  • 3,880
  • 1
  • 24
  • 38
Asaph
  • 159,146
  • 25
  • 197
  • 199
  • 6
    +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 Jan 01 '10 at 09:43
  • 3
    I upvoted your answer because I think you make a good point. If I had to guess why you got the downvote, it's probably because this doesn't answer the question. Q: "How do I do named parameters in Java?" A: "You don't" – Gattster Feb 12 '13 at 01:05
  • 16
    I downvoted because I think you answer has nothing to do with the question. Verbose names don't really solve the problem of parameter ordering. Yes, you could encode them in the name but that is clearly not practicable. Bringing up an unrelated paradigm doesn't explain why one paradigm is not supported. – Andreas Mueller Jun 03 '13 at 13:51
7

If you are using Java 6, you can use the variable parameters and import static to produce a much better result. Details of this are found in:

http://zinzel.blogspot.com/2010/07/creating-methods-with-named-parameters.html

In short, you could have something like:

go();
go(min(0));
go(min(0), max(100));
go(max(100), min(0));
go(prompt("Enter a value"), min(0), max(100));
R Casha
  • 79
  • 1
  • 1
  • 2
    I like it, but it still only solves half the problem. In Java, you cannot prevent accidentally transposed parameters without losing a compile-time check for required values. – cdunn2001 May 03 '12 at 16:53
  • 2
    Without type safety this is worse than simple //comments. – Peter Davis Mar 12 '13 at 04:55
7

What about

public class Tiger {
    String myColor;
    int myLegs;

    public Tiger color(String s)
    {
        myColor = s;
        return this;
    }

    public Tiger legs(int i)
    {
        myLegs = i;
        return this;
    }
}

Tiger t = new Tiger().legs(4).color("striped");
Michael Mior
  • 28,107
  • 9
  • 89
  • 113
user564819
  • 79
  • 1
  • 1
  • 7
    Builder is much better, because you can check some constraints on build(). But I also prefer shorter arguments without set/with prefix. – rkj Mar 05 '12 at 17:49
  • 5
    Also, the builder pattern is better because it allows you to make the built class (Tiger in this case) immutable. – Jeff Olson May 28 '13 at 21:22
4

I feel like the "comment-workaround" deserves it's own answer (hidden in existing answers and mentioned in comments here).

someMethod(/* width */ 1024, /* height */ 768);
Reto Höhener
  • 5,419
  • 4
  • 39
  • 79
2

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
  • 89,107
  • 111
  • 320
  • 448
  • 2
    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 Mar 29 '10 at 23:58
  • 1
    I would upvote this for the analysis: `not really better than a comment` ... on the other hand ... ;) – Scheintod Sep 16 '15 at 17:26
2

Using Java 8's lambdas you can get even closer to real named parameters.

foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});

Do note that this probably violates a couple dozen "java best practices" (like anything that makes use of the $ symbol).

public class Main {
  public static void main(String[] args) {
    // Usage
    foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});
    // Compare to roughly "equivalent" python call
    // foo(foo = -10, bar = "hello", array = [1, 2, 3, 4])
  }

  // Your parameter holder
  public static class $foo {
    private $foo() {}

    public int foo = 2;
    public String bar = "test";
    public int[] array = new int[]{};
  }

  // Some boilerplate logic
  public static void foo(Consumer<$foo> c) {
    $foo foo = new $foo();
    c.accept(foo);
    foo_impl(foo);
  }

  // Method with named parameters
  private static void foo_impl($foo par) {
    // Do something with your parameters
    System.out.println("foo: " + par.foo + ", bar: " + par.bar + ", array: " + Arrays.toString(par.array));
  }
}

Pros:

  • Considerably shorter than any builder pattern I've seen so far
  • Works for both methods and constructors
  • Completely type safe
  • It looks very close to actual named parameters in other programming languages
  • It's about as safe as your typical builder pattern (can set parameters multiple times)

Cons:

  • Your boss will probably lynch you for this
  • It's harder to tell what's going on
Vic
  • 139
  • 8
  • 1
    Cons: fields are public and not final. If you are ok with this, why not just use setters? How does it work for methods? – Alex Jan 20 '17 at 12:40
  • Could use setters but whats the point in that? It just makes the code longer and that would eliminate the benefit of doing it like this. Assignment is side effect free and setters are black boxes.`$foo` never escapes to the caller (unless someone does assign it to a variable inside of the callback) so why *can't* they be public? – Vic Feb 21 '17 at 15:25
2

Any solution in Java is likely going to be pretty verbose, but it's worth mentioning that tools like Google AutoValues and Immutables will generate builder classes for you automatically using JDK compile time annotation processing.

For my case, I wanted named parameters to use in a Java enum, so a builder pattern wouldn't work because enum instances can't be instantiated by other classes. I came up with an approach similar @deamon's answer but adds compile-time checking of parameter ordering (at the expense of more code)

Here's client code:

Person p = new Person( age(16), weight(100), heightInches(65) );

And the implementation:

class Person {
  static class TypedContainer<T> {
    T val;
    TypedContainer(T val) { this.val = val; }
  }
  static Age age(int age) { return new Age(age); }
  static class Age extends TypedContainer<Integer> {
    Age(Integer age) { super(age); }
  }
  static Weight weight(int weight) { return new Weight(weight); }
  static class Weight extends TypedContainer<Integer> {
    Weight(Integer weight) { super(weight); }
  }
  static Height heightInches(int height) { return new Height(height); }
  static class Height extends TypedContainer<Integer> {
    Height(Integer height) { super(height); }
  }

  private final int age;
  private final int weight;
  private final int height;

  Person(Age age, Weight weight, Height height) {
    this.age = age.val;
    this.weight = weight.val;
    this.height = height.val;
  }
  public int getAge() { return age; }
  public int getWeight() { return weight; }
  public int getHeight() { return height; }
}
scott
  • 1,003
  • 9
  • 7
2

You can use project Lombok's @Builder annotation to simulate named parameters in Java. This will generate a builder for you which you can use to create new instances of any class (both classes you've written and those coming from external libraries).

This is how to enable it on a class:

@Getter
@Builder
public class User {
    private final Long id;
    private final String name;
}

Afterwards you can use this by:

User userInstance = User.builder()
    .id(1L)
    .name("joe")
    .build();

If you'd like to create such a Builder for a class coming from a library, create an annotated static method like this:

class UserBuilder {
    @Builder(builderMethodName = "builder")
    public static LibraryUser newLibraryUser(Long id, String name) {
        return new LibraryUser(id, name);
    }
  }

This will generate a method named "builder" which can be called by:

LibraryUser user = UserBuilder.builder()
    .id(1L)
    .name("joe")
    .build();
Istvan Devai
  • 3,962
  • 23
  • 21
  • Google auto/value serves a similiar purpose but uses the annotation processing framework, which is much safer (stuff will still work after an upgrade of the JVM) than project Lombocks byte code manipulation. – René Jan 08 '19 at 15:50
  • 1
    I think the Google auto/value requires a bit of an extra mile compared to using Lombok. Lombok's approach is more or less compatible with traditional JavaBean writing (eg you can instantiate via new, fields show up properly in a debugger, etc.). I'm also not a big fan of the byte code manpiluation + IDE plugin solution what Lombok uses, but I have to admit, that in practice it works OK. No problems so far with JDK version changes, reflection, etc. – Istvan Devai Jan 09 '19 at 19:50
  • Yes, that is true. For auto/value you need to provide an abstract class that will then be implemented. Lombok needs a lot less code. So pros and cons need to be compared. – René Jan 09 '19 at 20:05
1

This is a variant of the Builder Pattern as described by Lawrence above.

I find myself using this a lot (at the apropriate places).

The main difference is, that in this case the Builder is immuatable. This has the advantage that it can be reused and is thread-safe.

So you can use this to make one default Builder and then in the various places where you need it you can configure it and build your object.

This makes most sense, if you are building the same object over and over again, because then you can make the builder static and don't have to worry about changing it's settings.

On the other hand if you have to build objects with changing paramaters this has quiet some overhead. (but hey, you can combine static / dynamic generation with custom build methods)

Here is the example code:

public class Car {

    public enum Color { white, red, green, blue, black };

    private final String brand;
    private final String name;
    private final Color color;
    private final int speed;

    private Car( CarBuilder builder ){
        this.brand = builder.brand;
        this.color = builder.color;
        this.speed = builder.speed;
        this.name = builder.name;
    }

    public static CarBuilder with() {
        return DEFAULT;
    }

    private static final CarBuilder DEFAULT = new CarBuilder(
            null, null, Color.white, 130
    );

    public static class CarBuilder {

        final String brand;
        final String name;
        final Color color;
        final int speed;

        private CarBuilder( String brand, String name, Color color, int speed ) {
            this.brand = brand;
            this.name = name;
            this.color = color;
            this.speed = speed;
        }
        public CarBuilder brand( String newBrand ) {
            return new CarBuilder( newBrand, name, color, speed );
        }
        public CarBuilder name( String newName ) {
            return new CarBuilder( brand, newName, color, speed );
        }
        public CarBuilder color( Color newColor ) {
            return new CarBuilder( brand, name, newColor, speed );
        }
        public CarBuilder speed( int newSpeed ) {
            return new CarBuilder( brand, name, color, newSpeed );
        }
        public Car build() {
            return new Car( this );
        }
    }

    public static void main( String [] args ) {

        Car porsche = Car.with()
                .brand( "Porsche" )
                .name( "Carrera" )
                .color( Color.red )
                .speed( 270 )
                .build()
                ;

        // -- or with one default builder

        CarBuilder ASSEMBLY_LINE = Car.with()
                .brand( "Jeep" )
                .name( "Cherokee" )
                .color( Color.green )
                .speed( 180 )
                ;

        for( ;; ) ASSEMBLY_LINE.build();

        // -- or with custom default builder:

        CarBuilder MERCEDES = Car.with()
                .brand( "Mercedes" )
                .color( Color.black )
                ;

        Car c230 = MERCEDES.name( "C230" ).speed( 180 ).build(),
            clk = MERCEDES.name( "CLK" ).speed( 240 ).build();

    }
}
Scheintod
  • 7,953
  • 9
  • 42
  • 61
1

Here is a compiler-checked Builder pattern. Caveats:

  • this can't prevent double assignment of an argument
  • you can't have a nice .build() method
  • one generic parameter per field

So you need something outside the class that will fail if not passed Builder<Yes, Yes, Yes>. See the getSum static method as an example.

class No {}
class Yes {}

class Builder<K1, K2, K3> {
  int arg1, arg2, arg3;

  Builder() {}

  static Builder<No, No, No> make() {
    return new Builder<No, No, No>();
  }

  @SuppressWarnings("unchecked")
  Builder<Yes, K2, K3> arg1(int val) {
    arg1 = val;
    return (Builder<Yes, K2, K3>) this;
  }

  @SuppressWarnings("unchecked")
  Builder<K1, Yes, K3> arg2(int val) {
    arg2 = val;
    return (Builder<K1, Yes, K3>) this;
  }

  @SuppressWarnings("unchecked")
  Builder<K1, K2, Yes> arg3(int val) {
    this.arg3 = val;
    return (Builder<K1, K2, Yes>) this;
  }

  static int getSum(Builder<Yes, Yes, Yes> build) {
    return build.arg1 + build.arg2 + build.arg3;
  }

  public static void main(String[] args) {
    // Compiles!
    int v1 = getSum(make().arg1(44).arg3(22).arg2(11));
    // Builder.java:40: error: incompatible types:
    // Builder<Yes,No,Yes> cannot be converted to Builder<Yes,Yes,Yes>
    int v2 = getSum(make().arg1(44).arg3(22));
    System.out.println("Got: " + v1 + " and " + v2);
  }
}

Caveats explained. Why no build method? The trouble is that it's going to be in the Builder class, and it will be parameterized with K1, K2, K3, etc. As the method itself has to compile, everything it calls must compile. So, generally, we can't put a compilation test in a method of the class itself.

For a similar reason, we can't prevent double assignment using a builder model.

Ben
  • 836
  • 8
  • 18
0

You can imitate named parameters applying this pattern:

public static class CarParameters {
        
    // to make it shorter getters and props are omitted
        
    public ModelParameter setName(String name) {
        this.name = name;
        return new ModelParameter();
    }
    public class ModelParameter {
        public PriceParameter setModel(String model) {
            CarParameters.this.model = model;
            return new PriceParameter();
        }
    }
    public class PriceParameter {
        public YearParameter setPrice(double price) {
            CarParameters.this.price = price;
            return new YearParameter();
        }
    }
    public class YearParameter {
        public ColorParameter setYear(int year) {
            CarParameters.this.year = year;
            return new ColorParameter();
        }
    }
    public class ColorParameter {
        public CarParameters setColor(Color color) {
            CarParameters.this.color = color;
            return new CarParameters();
        }
    }
}

and then you can pass it to your method as this:

factory.create(new CarParameters()
        .setName("Ford")
        .setModel("Focus")
        .setPrice(20000)
        .setYear(2011)
        .setColor(BLUE));

You can read more here https://medium.com/@ivorobioff/named-parameters-in-java-9072862cfc8c

Next Developer
  • 1,249
  • 1
  • 10
  • 20
0

Now that we're all on Java 17 ;-), using records is a super-easy way to imitate this idiom:

public class OrderTemplate() {
    private int tradeSize, limitDistance, backoffDistance;

    public record TradeSize( int value ) {}
    public record LimitDistance( int value ) {}
    public record BackoffDistance( int value ) {}
    public OrderTemplate( TradeSize t, LimitDistance d, BackoffDistance b ) {
      this.tradeSize = t.value();
      this.limitDistance = d.value();
      this.backoffDistance = b.value();
    }
}

Then you can call:

var t = new OrderTemplate( new TradeSize(30), new LimitDistance(182), new BackoffDistance(85) );

Which I've found extremely easy to read and I've completely stopped getting all the int parameters mixed up ("was it size first or distance...").

mmarkholt
  • 1
  • 1
0
package org.xxx.lang;

/**
 * A hack to work around the fact that java does not support
 * named parameters in function calls.
 * 
 * Its easy to swap a few String parameters, for example.
 * Some IDEs are better than others than showing the parameter names.
 * This will enforce a compiler error on an inadvertent swap. 
 *
 * @param <T>
 */
public class Datum<T> {

    public final T v;
    
    public Datum(T v) {
        this.v = v;
    }
    
    public T v() {
        return v;
    }
    
    public T value() {
        return v;
    }
    
    public String toString() {
        return v.toString();
    }
}

Example

class Catalog extends Datum<String> {
        public Catalog(String v) {
            super(v);
        }
    }

    class Schema extends Datum<String> {
        public Schema(String v) {
            super(v);
        }
    }
class Meta {
        public void getTables(String catalog, String schema, String tablePattern) {
            // pseudo DatabaseMetaData.getTables();
        }
    }
    
    class MetaChecked {
        public void getTables(Catalog catalog, Schema schema, String tablePattern) {
            // pseudo DatabaseMetaData.getTables();
        }
    }

    @Test
    public void test() {
        Catalog c = new Catalog("test");
        assertEquals("test",c.v);
        assertEquals("test",c.v());
        assertEquals("test",c.value());
        String t = c.v;
        assertEquals("test",t);
    }

    public void uncheckedExample() {
        new Meta().getTables("schema","catalog","%"); 
        new Meta().getTables("catalog","schema","%"); // ooops
    }
    
    public void checkedExample() {
         // new MetaChecked().getTables(new Schema("schema"),new Catalog("catalog"),"%"); // won't compile
          new MetaChecked().getTables(new Catalog("catalog"), new Schema("schema"),"%"); 
    }
0

maybe can use this:

HashMapFlow<String,Object>  args2 = HashMapFlow.of( "name", "Aton", "age", 21 );
    Integer age = args2.get("age",51);
    System.out.println(args2.get("name"));
    System.out.println(age);
    System.out.println((Integer)args2.get("dayOfBirth",26));

class:

import java.util.HashMap;

public class HashMapFlow<K,V> extends HashMap {

    public static <K, V> HashMapFlow<K, V> of(Object... args) {
        HashMapFlow<K, V> map = new HashMapFlow();
        for( int i = 0; i < args.length; i+=2) {
            map.put((K)args[i], (V)args[i+1]);
        }
        return map;
    }

    public <T> T get(Object key, V defaultValue) {
        V result = (V)get(key);
        if( result == null ) {
            result = defaultValue;
        }
        return (T)result;
    }

    public HashMapFlow add(K key, V value) {
        put(key,value);
        return this;
    }
}

0

The idiom supported by the karg library may be worth considering:

class Example {

    private static final Keyword<String> GREETING = Keyword.newKeyword();
    private static final Keyword<String> NAME = Keyword.newKeyword();

    public void greet(KeywordArgument...argArray) {
        KeywordArguments args = KeywordArguments.of(argArray);
        String greeting = GREETING.from(args, "Hello");
        String name = NAME.from(args, "World");
        System.out.println(String.format("%s, %s!", greeting, name));
    }

    public void sayHello() {
        greet();
    }

    public void sayGoodbye() {
        greet(GREETING.of("Goodbye");
    }

    public void campItUp() {
        greet(NAME.of("Sailor");
    }
}
Dominic Fox
  • 1,039
  • 11
  • 8
-1

@irreputable came up with a nice solution. However - it might leave your Class instance in a invalid state, as no validation and consistency checking will happen. Hence I prefer to combine this with the Builder solution, avoiding the extra subclass to be created, although it would still subclass the builder class. Additionally, because the extra builder class makes it more verbose, I added one more method using a lambda. I added some of the other builder approaches for completeness.

Starting with a class as follows:

public class Foo {
  static public class Builder {
    public int size;
    public Color color;
    public String name;
    public Builder() { size = 0; color = Color.RED; name = null; }
    private Builder self() { return this; }

    public Builder size(int size) {this.size = size; return self();}
    public Builder color(Color color) {this.color = color; return self();}
    public Builder name(String name) {this.name = name; return self();}

    public Foo build() {return new Foo(this);}
  }

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

  public Foo(Builder b) {
    this.size = b.size;
    this.color = b.color;
    this.name = b.name;
  }

  public Foo(java.util.function.Consumer<Builder> bc) {
    Builder b = new Builder();
    bc.accept(b);
    this.size = b.size;
    this.color = b.color;
    this.name = b.name;
  }

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

  public int getSize() { return this.size; }
  public Color getColor() { return this.color; }  
  public String getName() { return this.name; }  

}

Then using this applying the different methods:

Foo m1 = new Foo(
  new Foo.Builder ()
  .size(1)
  .color(BLUE)
  .name("Fred")
);

Foo m2 = new Foo.Builder()
  .size(1)
  .color(BLUE)
  .name("Fred")
  .build();

Foo m3 = Foo.with()
  .size(1)
  .color(BLUE)
  .name("Fred")
  .build();

Foo m4 = new Foo(
  new Foo.Builder() {{
    size = 1;
    color = BLUE;
    name = "Fred";
  }}
);

Foo m5 = new Foo(
  (b)->{
    b.size = 1;
    b.color = BLUE;
    b.name = "Fred";
  }
);

It looks like in part a total rip-off from what @LaurenceGonsalves already posted, but you will see the small difference in convention chosen.

I am wonder, if JLS would ever implement named parameters, how they would do it? Would they be extending on one of the existing idioms by providing a short-form support for it? Also how does Scala support named parameters?

Hmmm - enough to research, and maybe a new question.

YoYo
  • 9,157
  • 8
  • 57
  • 74