{"id":16782590,"url":"https://github.com/davidmoten/java-builder-pattern-tricks","last_synced_at":"2025-05-08T16:58:18.856Z","repository":{"id":139309210,"uuid":"111151486","full_name":"davidmoten/java-builder-pattern-tricks","owner":"davidmoten","description":"Tricks to use with the java builder pattern","archived":false,"fork":false,"pushed_at":"2024-05-20T21:43:38.000Z","size":62,"stargazers_count":53,"open_issues_count":0,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-08T16:58:13.503Z","etag":null,"topics":["builder-pattern","java","tutorial"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidmoten.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-17T21:17:13.000Z","updated_at":"2025-01-29T04:14:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"09ee104a-dfd5-4b45-b4c2-175a70894913","html_url":"https://github.com/davidmoten/java-builder-pattern-tricks","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fjava-builder-pattern-tricks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fjava-builder-pattern-tricks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fjava-builder-pattern-tricks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fjava-builder-pattern-tricks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidmoten","download_url":"https://codeload.github.com/davidmoten/java-builder-pattern-tricks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253112069,"owners_count":21856070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["builder-pattern","java","tutorial"],"created_at":"2024-10-13T07:46:24.151Z","updated_at":"2025-05-08T16:58:18.821Z","avatar_url":"https://github.com/davidmoten.png","language":null,"readme":"# java-builder-pattern-tricks\nThe humble java builder pattern has been described frequently but is nearly always described in a very basic form without revealing its true potential! If you push the pattern a bit harder then you get can less verbosity in your API and more compile-time safety.\n\nSo what are these extra tricks?\n\n * [Format the code better (long method chained lines are yuk!)](#trick-1-formatting)\n * [Shortcut the `builder()` method](#shortcut-the-builder-method)\n * [Enforce mandatory parameters at compile time with *builder chaining*](#trick-3-enforce-mandatory-fields-at-compile-time-with-builder-chaining)\n * [Remove final `build()` call when all fields mandatory](#trick-4-remove-final-build-call-when-all-fields-mandatory)\n * [Build generic signatures with builder chaining](#trick-5-build-generic-signatures)\n * [Improve discoverability](#trick-6-improve-discoverability)\n * [Build lists succinctly](#trick-7-build-lists-succinctly)\n \n The open-source library [rxjava2-jdbc](https://github.com/davidmoten/rxjava2-jdbc) uses all these tricks to make the API easier to use. See also [openapi-codegen](https://github.com/davidmoten/openapi-codegen) and [odata-client](https://github.com/davidmoten/odata-client).\n \n[java-builder2](https://github.com/davidmoten/java-builder2) is a library to generate builders.\n\n## What's the basic builder pattern for?\n* constructor field assignments can be mixed up\n* constructor calls are not very readable because Java doesn't support named parameters\n\n## The basic builder pattern\nLet's start with a basic builder pattern and then we'll improve it. We'll consider how to build a `Book` object.\n\nOur `Book` object has:\n* a mandatory `author` field\n* a mandatory `title` field\n* an optional `category` field\n\n```java\npublic final class Book {\n    // Make fields final so we always know we've missed assigning one in the constructor!\n    private final String author;\n    private final String title;\n    private final Optional\u003cString\u003e category;\n    \n    //should not be public\n    private Book(Builder builder) {\n        //Be a bit defensive\n        Preconditions.checkNotNull(builder.author);\n        Preconditions.checkArgument(builder.author.trim().length() \u003e 0);\n        Preconditions.checkNotNull(builder.title);\n        Preconditions.checkArgument(builder.title.trim().length() \u003e 0);\n        Preconditions.checkNotNull(category);\n        this.author = builder.author;\n        this.title = builder.title;\n        this.category = builder.category;\n    }\n    \n    public String author() {\n        return author;\n    }\n    \n    public String title() {\n        return title;\n    }\n    \n    public Optional\u003cCategory\u003e category() {\n       return category;\n    }\n    \n    public static Builder builder() {\n        return new Builder();\n    }\n    \n    public static final class Builder {\n        String author;\n        String title;\n        Optional\u003cString\u003e category = Optional.empty();\n        \n        // should not be public to force use of the static builder() method\n        private Builder() {\n        }\n        \n        public Builder author(String author) {\n            this.author = author;\n            return this;\n        }\n        \n        public Builder title(String title) {\n            this.title = title;\n            return this;\n        }\n        \n        public Builder category(String category) {\n            this.category = Optional.of(category);\n            return this;\n        }\n        \n        public Book build() {\n            return new Book(this);\n        }   \n    }\n}\n```\n\nTo use this basic builder:\n\n```java\nBook book = Book.builder().author(\"Charles Dickens\").title(\"Great Expectations\")\n    .category(\"Novel\").build();\n```\nNote that I haven't prefixed the methods with `set` or `with`. Seems like pointless noise to me but go with whatever style you like.\n\nSo that example looks ok but we can do better. \n\n## Making the basic builder better\n### Trick 1: Formatting\nThe first thing I'll note is that when you're chaining methods you end up with something much more readable when you put one method per line. You can force an IDE to honour this when it formats code by **adding an empty comment at the end of each line**:\n\n```java\nBook book = Book //\n  .builder() //\n  .author(\"Charles Dickens\") //\n  .title(\"Great Expectations\") //\n  .category(\"Novel\") //\n  .build();\n```\n\n### Trick 2: Shortcut the builder() method\n\nThe `builder()` method call is unnecessary if you have a mandatory field. We can replace that call with a call to one or many of the mandatory fields like `author`:\n\nSo instead of the declaration:\n\n```java\npublic Builder builder() {\n    return new Builder();\n}\n```\nwe put:\n```java\npublic Builder author(String author) {\n     return new Builder().author(author);\n}\n```\nand we reduce the visibility of the `Builder.author(String)` method (because it has already been set).\n\nNow we can call:\n\n```java\nBook book = Book //\n  .author(\"Charles Dickens\") //\n  .title(\"Great Expectations\") //\n  .category(\"Novel\") //\n  .build();\n```\nSaved one line, nice!\n\nBy incorporating `author` as a final field in the builder and passing it in the constructor we can clean up the internals a bit more:\n\n```java\npublic final class Book {\n    // Make fields final so we always know we've missed assigning one in the constructor!\n    private final String author;\n    private final String title;\n    private final Optional\u003cString\u003e category;\n    \n    private Book(Builder builder) {\n        //Be a bit defensive\n        Preconditions.checkNotNull(builder.author);\n        Preconditions.checkArgument(builder.author.trim().length() \u003e 0);\n        Preconditions.checkNotNull(builder.title);\n        Preconditions.checkArgument(builder.title.trim().length() \u003e 0);\n        Preconditions.checkNotNull(category);\n        this.author = builder.author;\n        this.title = builder.title;\n        this.category = builder.category;\n    }\n    \n    public String author() {\n        return author;\n    }\n    \n    public String title() {\n        return title();\n    }\n    \n    public Optional\u003cCategory\u003e category() {\n       return category;\n    }\n    \n    public static Builder author(String author) {\n        return new Builder(author);\n    }\n    \n    public static final class Builder {\n        final String author;\n        String title;\n        Optional\u003cString\u003e category = Optional.empty();\n        \n        private Builder(String author) {\n            this.author = author;\n        }\n       \n        public Builder title(String title) {\n            this.title = title;\n            return this;\n        }\n        \n        public Builder category(String category) {\n            this.category = Optional.of(category);\n            return this;\n        }\n        \n        public Book build() {\n            return new Book(this);\n        }     \n    }\n}\n```\n\nNote that you may prefer to continue using the `builder()` method particularly if the class already has a number of public static methods and distinguishing your builder entry method is difficult.\n\n### Trick 3: Enforce mandatory fields at compile time with builder chaining\nNow let's improve the builder some more. We have to consider the `Book` object itself. It has two mandatory fields `author` and `title` and one optional field `category`. \n\nAs the builder stands so far we have a runtime check on the mandatory fields:\n\n```java\n// will throw NullPointerException because title missing!\nBook book = Book\n  .author(\"Charles Dickens\")\n  .build();\n```\nIt would be even better to get compile-time indication and this is something we can achieve with the *builder chaining* trick:\n\n```java\npublic final class Book {\n    // Make fields final!\n    private final String author;\n    private final String title;\n    private final Optional\u003cString\u003e category;\n    \n    private Book(Builder builder) {\n        //Be a bit defensive\n        Preconditions.checkNotNull(builder.author);\n        Preconditions.checkArgument(builder.author.trim().length() \u003e 0);\n        Preconditions.checkNotNull(builder.title);\n        Preconditions.checkArgument(builder.title.trim().length() \u003e 0);\n        Preconditions.checkNotNull(category);\n        this.author = builder.author;\n        this.title = title;\n        this.category = category;\n    }\n    \n    public String author() {\n        return author;\n    }\n    \n    public String title() {\n        return title();\n    }\n    \n    public Optional\u003cCategory\u003e category() {\n       return category;\n    }\n    \n    public static BuilderHasAuthor author(String author) {\n        return new BuilderHasAuthor(author);\n    }\n    \n    public static final class BuilderHasAuthor {\n        final String author;\n        String title;\n        Optional\u003cString\u003e category = Optional.empty();\n        \n        private BuilderHasAuthor(String author) {\n            this.author = author;\n        }\n        \n        BuilderHasTitle title(String title) {\n            this.title = title;\n            return new Builder2(this);\n        }\n    }\n    \n    public static final class BuilderHasTitle {\n        final BuilderHasAuthor b;\n        \n        BuilderHasTitle(BuilderHasAuthor b) {\n            this.b = b;\n        }\n        \n        BuilderHasTitle category(String category) {\n            b.category = Optional.of(category);\n            return this;\n        }\n                \n        public Book build() {\n            return new Book(b);\n        }     \n    }\n}\n```\nNow if we try the previous example it won't compile. That's really good because finding bugs at compile time is MUCH better than finding out at runtime!\n\n```java\n// will not compile!\nBook book = Book\n  .author(\"Charles Dickens\")\n  .build();\n```\n\nIn fact, the new builder forces us to follow an exact method order in that you can't swap the order of `author`,`title` and `category` methods.\n\n```java\nBook book = Book\n  .author(\"Charles Dickens\")\n  .title(\"Great Expectations\")\n  .category(\"Novel\")\n  .build();\n```\n\nHey `category` is an optional parameter isn't it? Let's test that:\n\n```java\nBook book = Book\n  .author(\"Charles Dickens\")\n  .title(\"Great Expectations\")\n  .build();\n```\nYep that compiles, no problem.\n\nNote also the naming standard `BuilderHas*` that helps readability.\n\n### Trick 4: Remove final build() call when all fields mandatory\nSee that final `.build()` method? Sometimes we can get rid of that too. If every field was mandatory and they were all builder chained then you could do this:\n\n```java\nBook book = Book\n  .author(\"Charles Dickens\")\n  .title(\"Great Expectations\")\n  .category(\"Novel\");\n```\n\nThat's one less line of code again which is nice but can only be achieved when all fields are mandatory.\n\nTo achieve this just replace this code:\n\n```java\n        Builder2 category(String category) {\n            b.category = Optional.of(category);\n            return this;\n        }\n                \n        public Book build() {\n            return new Book(b);\n        }   \n```\nwith\n```java\n        Book category(String category) {\n            b.category = Optional.of(category);\n            return new Book(b);\n        }\n```\n\n### Trick 5: Build generic signatures\nTo demonstrate the building of generic signatures, we'll show a contrived example with *Tuples*.\n\nI want a class now that builds typed *Tuples* but I want a standard build method that honours generic types:\n\n```java\nType2\u003cInteger, String\u003e t2 = Tuples\n  .value(12)\n  .value(\"thing\")\n  .build();\nType3\u003cInteger, String, Date\u003e t3 = Tuples\n  .value(12)\n  .value(\"thing\")\n  .value(new Date())\n  .build(); \n```\nThis api is achieved using builder chaining where each chained builder adds another generic signature:\n\n```java\npublic final class Tuple2\u003cA,B\u003e {\n    public final A value1;\n    public final B value2;\n    public Tuple2(A value1, B value2) {\n      this.value1 = value1;\n      this.value2 = value2;\n    }\n}\n\npublic final class Tuple3\u003cA,B,C\u003e {\n    public final A value1;\n    public final B value2;\n    public final C value3;\n    public Tuple2(A value1, B value2, C value3) {\n      this.value1 = value1;\n      this.value2 = value2;\n      this.value3 = value3;\n    }\n}\n\npublic final class Tuples {\n\n    public static \u003cT\u003e Builder1\u003cT\u003e value(T t){\n        return new Builder1\u003cT\u003e(t);\n    }\n    \n    public static final class Builder1\u003cA\u003e {\n        private final A value1;\n        private Builder1(A value1) {\n            this.value1 = value1;\n        }\n        public \u003cB\u003e value(A value2) {\n            return new Builder2\u003cA, B\u003e(value1, value2);\n        }\n    }\n    \n    public static final class Builder2\u003cA, B\u003e {\n        private final A value1;\n        private final B value2;\n        private Builder2(A value1, B value2) {\n            this.value1 = value1;\n            this.value2 = value2;\n        }\n        public \u003cC\u003e Builder3\u003cA, B, C\u003e value(C value3) {\n            return new Builder3\u003cA, B, C\u003e(value1, value2);\n        }\n        \n        public Tuple2\u003cA,B\u003e build() {\n            return new Tuple2\u003cA,B\u003e(value1, value2);\n        }\n    }\n    \n    public static final class Builder3\u003cA, B, C\u003e {\n        private final A value1;\n        private final B value2;\n        private final C value3;\n        private Builder3(A value1, B value2, C value3) {\n            this.value1 = value1;\n            this.value2 = value2;\n            this.value3 = value3;\n        }\n        \n        //could add a Builder4 to keep chaining!\n        \n        public Tuple3\u003cA, B, C\u003e build() {\n            return new Tuple3\u003cA, B, C\u003e(value1, value2, value3);\n        }\n    }\n}\n```\n### Trick 6: Improve discoverability\nIf your builder is to create a `Thing` then ideally they should be able to start the builder via a static method on `Thing`:\n\n```java\nThing thing = Thing.name(\"FRED\").sizeMetres(1.5).create();\n```\nIf you can't add a method to `Thing` (you might not own the api) then favour use of `Things`:\n\n```java\nThing thing = Things.name(\"FRED\").sizeMetres(1.5).create();\n```\nFor clarity reasons (for example if the class already has a lot of public static methods) if you want to use a `builder` method then go for it:\n\n```java\nThing thing = Thing.builder().name(\"FRED\").sizeMetres(1.5).create();\n```\n### Trick 7: Build lists succinctly\nUsing a special chaining trick we can support the building of lists like this:\n\n```java\nGroup\n  .name(\"friends\")\n  .firstName(\"John\")\n  .lastName(\"Smith\")\n  .yearOfBirth(1965)\n  .firstName(\"Anne\")\n  .lastName(\"Jones\")\n  .build();\n```\nIn the example above `firstName` and `lastName` is mandatory and `yearOfBirth` is optional.\n\nYou see that we didn't need to return back to the top level builder to add the next person. This is achieved by adding methods pointing back to the top level builder from the person builder both for creating the next person but also for building the group if the list has finished. Here's all the code:\n\n```java\npublic final class Group {\n\n    private final String name;\n    private final List\u003cPerson\u003e persons;\n\n    public Group(String name, List\u003cPerson\u003e persons) {\n        this.name = name;\n        this.persons = persons;\n    }\n\n    public String name() {\n        return name;\n    }\n\n    public List\u003cPerson\u003e persons() {\n        return persons;\n    }\n\n    public static Builder name(String name) {\n        Preconditions.checkNotNull(name);\n        return new Builder(name);\n    }\n\n    public static final class Builder {\n        final String name;\n        List\u003cPerson\u003e persons = new ArrayList\u003c\u003e();\n\n        Builder(String name) {\n            this.name = name;\n        }\n\n        public Builder2 firstName(String firstName) {\n            Preconditions.checkNotNull(firstName);\n            return new Builder2(this, firstName);\n        }\n\n        public Group build() {\n            return new Group(name, persons);\n        }\n    }\n\n    public static final class Builder2 {\n\n        private final Builder b;\n        private final String firstName;\n        private String lastName;\n        private Optional\u003cInteger\u003e yearOfBirth = Optional.empty();\n\n        Builder2(Builder b, String firstName) {\n            this.b = b;\n            this.firstName = firstName;trick-7-build-lists-succintly\n        }\n\n        public Builder3 lastName(String lastName) {\n            Preconditions.checkNotNull(firstName);\n            this.lastName = lastName;\n            return new Builder3(this);\n        }\n    }\n\n    public static final class Builder3 {\n\n        private final Builder2 person;\n\n        Builder3(Builder2 person) {\n            this.person = person;\n        }\n\n        public Builder3 yearOfBirth(int yearOfBirth) {\n            person.yearOfBirth = Optional.of(yearOfBirth);\n            return this;\n        }\n\n        public Builder2 firstName(String firstName) {\n            Person p = new Person(person.firstName, person.lastName, person.yearOfBirth);\n            person.b.persons.add(p);\n            return person.b.firstName(firstName);\n        }\n\n        public Group build() {\n            return person.b.build();\n        }\n    }\n\n    public static final class Person {\n        private final String firstName;\n        private final String lastName;\n\n        private final Optional\u003cInteger\u003e yearOfBirth;\n\n        public Person(String firstName, String lastName, Optional\u003cInteger\u003e yearOfBirth) {\n            this.firstName = firstName;\n            this.lastName = lastName;\n            this.yearOfBirth = yearOfBirth;\n        }\n\n        public String firstName() {\n            return firstName;\n        }\n\n        public String lastName() {\n            return lastName;\n        }\n\n        public Optional\u003cInteger\u003e yearOfBirth() {\n            return yearOfBirth;\n        }\n    }\n}\n```\n\n## Conclusion\nWith luck you've seen that there is more power to the builder pattern than you realized and users of your APIs will benefit!\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Fjava-builder-pattern-tricks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidmoten%2Fjava-builder-pattern-tricks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Fjava-builder-pattern-tricks/lists"}