user3810626
user3810626

Reputation: 7699

Java Record Custom Constructor Selective Parameters

I have a record:

public record Thing(
   Boolean field1,
   EnumType field2,
   int field3,
   String, field4
   . . .

I'm adding new fields to Thing a lot, which breaks all my code that uses the canonical constructor, and the fields are piling up, which makes the canonical constructor increasingly awkward to use.

What I'd like to do is be able to pass in specific values for arbitrary fields:

new Thing(field3 = 1, field4 = "XXX");
new Thing(field1 = true, field2 = ENUMVALUE);

Now, I know I can do constructors like:

Thing(int i, String s) ...
Thing(Boolean b, EnumType, e) ...
. . .
new Thing(1, "XXX");
new Thing(true, ENUMVALUE);

But it's really a situation of not knowing which fields I want to override the default values for. The only thing I've been able to come up with so far is turning Thing into a regular object so I can do something like:

new Thing().setField1(true).setField4("Blah");

Which is kind of a bummer because I don't want the values to be mutable once initialized.

Upvotes: 4

Views: 7710

Answers (1)

Ervin Szilagyi
Ervin Szilagyi

Reputation: 16805

Unfortunately Java does not have named arguments (at least at this point in time). What we can do to overcome this missing language feature is to use the builder pattern.

For records we could do something like this:

public record Thing(Boolean field, int field2, String field3) {

    private Thing(ThingBuilder thingBuilder) {
        this(thingBuilder.field, thingBuilder.field2, thingBuilder.field3);
    }

    public static class ThingBuilder {
        // We can set default values for the fields
        private Boolean field = false;
        private int field2;
        private String field3 = "";

        public ThingBuilder() {}

        public ThingBuilder field(Boolean field) {
            this.field = field;
            return this;
        }

        public ThingBuilder field2(int field) {
            this.field2 = field;
            return this;
        }

        public ThingBuilder field3(String field) {
            this.field3 = field;
            return this;
        }

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

We can build Thing instances in the following way:

Thing thing = new Thing.ThingBuilder().field(true).field2(2).field3("some string").build();

Also, we can omit some fields and have them use their default values:

Thing thing = new Thing.ThingBuilder().field3("some string").build();

Upvotes: 7

Related Questions