Cascading Lambdas and Builder Pattern in Java, when 1 + 1 = 3… but not 4

Cascading Lambdas and Builder Pattern in Java, when 1 + 1 = 3… but not 4

The builder pattern is one of many well known design patterns used to manage complexity in our code. In particular, it helps us to separate an object construction from its representation, which is especially useful when we have to construct an object with a large number of fields. But what it doesn’t do well is to discriminate between mandatory and optional fields at construction time. This post will explore different ways to overcome this limitation and introduce a Java 8 specific feature to hopefully make the builder pattern implementation more robust while keeping the code simple and concise.

Let’s first start by assuming we want to create an object of type Person, which could look something like:

For brevity we only declare 3 fields here, but we could easily have 10-12 fields (although too many declared fields might also be a code smell), which is one of the problems the builder pattern solves by avoiding the explosion of constructor parameter combinations (assuming not all fields are mandatory). Above is a typical immutable data class (all fields are final, no setter method), the constructor is also private as we want the class to only be instantiated by the builder. In the example above, we notice from the getter method signatures that we want dateOfBirth to be optional and firstName and lastName to be mandatory.

So how can we force the creation of Person with firstName and lastName while leaving the possibility of not initializing the dateOfBirth? We could use a typical variation of the builder pattern, called the Step Builder:

By making PersonBuilder implement FirstNameBuilder and LastNameBuilder interfaces, also by making its constructor private and by exposing only FirstNameBuilder as an entry point through the builder() static method, we indeed force the user to initialize firstName and lastName before making the build() method available. The following is an example of how the builder would be invoked:

So we are done! Or are we? On the surface everything looks good now, the user of our builder cannot forget to call neither firstName() nor lastName(), those rules now being enforced by the compiler. Here we are assuming the user of our builder acts as a good citizen with our api and will obey the to rules we just set. But what if the user of our builder decides to go rogue and tries to trick us? In the spirit of never underestimating the creativity and determination of people wanting to do bad things (in coding or in life in general as a matter of fact), is there a way to bypass the safeguards we just implemented above and instantiate a Person object through the builder without first calling firstName() and lastName()?

Looks like the answer is “yes”. A simple cast to PersonBuilder is enough to bypass all the rules we tried to enforce above. So how can we prevent this? We need to return an implementation of FirstNameBuilder from Person.builder() that doesn’t declare the build() method. We also need to do the same with LastNameBuilder. But if we have to declare a new class for each mandatory parameter exposed by our builder, our code will start to get pretty bloated and hard to follow. So how can be achieve this in a more concise and elegant way?

Introducing Cascading Lambdas

I invite you to read this excellent and very detailed article from Venkat Subramaniam which explains in a very accessible way what are Cascading Lambas, but for the scope of this blog post, it could be very quickly summarized as the ability to have functions that can return other functions. If you notice from above, FirstNameBuilder and LastNameBuilder are interfaces declaring a single abstract method, thus they are considered functional interfaces since Java 8. We can also see that the initial Person.builder() call returns a functional interface (FirstNameBuilder) which in turn returns another functional interface (LastNameBuilder). By mapping those functional interfaces to lambda expressions, we indeed get a function that returns another function.

So how this can be combined with the builder pattern we created above :

Our builder() method highlighted above uses cascading lambdas to chain the calls to set our mandatory parameters firstName() and lastName(). This can be read as “create a function that takes parameter firstName and return another function, that new function taking parameter lastName and returns a PersonBuilder“. Only when we have an instance of PersonBuilder that the build() method is made available to instantiate a Person object. See below how our interfaces are being made available as we chain the method calls when we instantiate a Person:

We make the final build() method available only once all our mandatory parameters are set, we also prevent cheating from prematurely get access to the build() method through explicit casting:

So there you have it, by combining the builder pattern with cascading lambdas (1 + 1), we get much safer, concise and readable code (3), which hopefully is of bigger value than the sum of each part… Speaking of this blog post’s title, why 1 + 1 = 3… but not 4? The code above lets the compiler itself prevent the coder from making mistakes that would normally show up at runtime, it also cuts the number of runtime checks that has to be implemented, but not all of them. We still need to check at runtime for null values explicitly passed as our mandatory parameters firstName() and lastName() since the following is still possible:

It would be nice to also be able to catch this scenario at compile time… which is possible… in Kotlin!

9 Replies to “Cascading Lambdas and Builder Pattern in Java, when 1 + 1 = 3… but not 4”

  1. You don’t really need cascading lambdas, or a curried builder, like that. It can get quite unwieldy for more fields. Just returning method references from builder methods would be enough. And it would also allow for fine-grained invariants over multiple sets of fields…

    1. Thanks Charlie for taking time to read my post, constructive comments are always appreciated, if you have time and can provide a small code snippet to illustrate your argument that would be very appreciated, I want to make sure I correctly understand and don’t misinterpret the argument you are trying to make before I try to reply to the specifics of your comment. I’ll say this in general though (which might not apply to your comment), my philosophy is to try to keep it as simple as possible for the user of the api, sometimes at the expense of adding a bit of complexity in the implementation. For example, one @FunctionalInterface per mandatory parameter (not optional parameters) could make the implementation of the builder a bit bloated as more and more mandatory parameters are needed, but this makes it easier and more natural for the caller of the builder e.g.

      Chaining (or cascading) those @FunctionalInterfaces also make it possible to never make the build() method of the builder visible until all mandatory parameters are set.

      1. Well, my argument was not against chaining restricted views of the builder via functional interfaces. Just that the same can be achieved via a maybe-more-extensible way with method references:


        class Person {
        static FirstNameBuilder builder() {
        return new PersonBuilder()::firstName;
        }

        static class PersonBuilder {
        LastNameBuilder firstName(String firstName) {
        this.firstName = firstName;
        return this::lastName;
        }
        GenderBuilder lastName(String lastName) {
        this.lastName = lastName;
        return this::gender;
        }
        PersonBuilder gender(Gender gender) {
        this.gender = gender;
        return this;
        }
        // ...
        }
        }

        1. I’m glad you provided a small code example as I clearly misunderstood the point you were trying to make in your initial comment. This is a very interesting approach indeed, the only thing I can see is the fact that we could then call multiple times the same methods for mandatory fields. If we use the following as an example:

          the only minor difference is that we could then potentially do something like:

          which would not compile with the cascading lambdas (although we can’t avoid this scenario anyway with optional fields e.g. dateOfBirth), but otherwise I really like your idea of returning method references in general when we can, it’s not only useful as input parameters to methods and can make the code much cleaner!

          1. You can set scope of all setters to private and example with method references will be working as lambda chain example 🙂

          2. You are right, by changing the access level of the mandatory fields setters to private (firstName() and lastName() here) on PersonBuilder, we can achieve the same result as going the cascading lambdas route :). At this point only accessing the setters on the PersonBuilder through reflection (with setAccessible(true)) would bypass the mechanism, which would not be possible with cascading lambas as firstName() and lastName() would not even exist, but here we are crossing the line of what is reasonable ;). Thanks to you and Charlie for your contributions, that is also why we write blog posts, to generate discussions that can lead to alternative ways of doing things, this was a good one!

  2. Thanks, it’s really nice and useful article, to check for nulls at runtime you can try this:

    public static FirstNameBuilder builder() {
    return firstName -> lastName -> new PersonBuilder(Objects.requireNonNull(firstName), Objects.requireNonNull(lastName));
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: