Builder Pattern with Java Records

Java Records, introduced in Java 14, brought a streamlined approach to defining immutable data classes. While records inherently simplify data class creation, combining them with the Builder Pattern can further enhance their usability, especially for complex objects with numerous fields or optional parameters. In this blog post, we'll explore how the Builder Pattern can be implemented with Java Records, providing a robust solution for more complex use cases. 

Understanding Java Records and the Builder Pattern 

Java Records are a concise way to create immutable data structures, automatically generating constructors, getters, and standard methods like equals(), hashCode(), and toString(). However, when dealing with objects that have many fields or optional fields, using the canonical constructor directly can become cumbersome and less readable. 

The Builder Pattern comes to the rescue here. It allows step-by-step construction of complex objects and can be particularly useful when you have several optional parameters. 

Implementing Builder Pattern with Java Records 

Let's create an example to demonstrate how we can combine Java Records with the Builder Pattern for a more flexible object creation process. 

Step 1: Define the Record

We start by defining a Product record with multiple fields.

public record Product(Long id, String name, String description, Double price, String category) {}

Step 2: Create the Builder Class 

We then create a static inner builder class inside the Product record.
public record Product(Long id, String name, String description, Double price, String category) {

    public static class Builder {
        private Long id;
        private String name;
        private String description;
        private Double price;
        private String category;

        public Builder id(Long id) { this.id = id; return this; }
        public Builder name(String name) { this.name = name; return this; }
        public Builder description(String description) { this.description = description; return this; }
        public Builder price(Double price) { this.price = price; return this; }
        public Builder category(String category) { this.category = category; return this; }

        public Product build() {
            return new Product(id, name, description, price, category);
        }
    }
}

Step 3: Using the Builder 

With the builder in place, creating a Product record becomes more intuitive, especially when not all fields are required.
public class Main {
    public static void main(String[] args) {
        Product product = new Product.Builder()
                .name("Coffee Mug")
                .price(15.99)
                .build();

        System.out.println(product);
    }
}

Output:

Product[id=null, name=Coffee Mug, description=null, price=15.99, category=null]

More Examples

This Java program demonstrates the use of the Builder pattern in combination with Java records.

public record Person(String name, int age, String email) {
    // Nested Builder class for Person
    public static class Builder {
        private String name;
        private int age;
        private String email;

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

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

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

        public Person build() {
            // Validation logic can be added here
            return new Person(name, age, email);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // Building a Person object using the Builder
        Person person = new Person.Builder()
                            .name("Alice")
                            .age(30)
                            .email("[email protected]")
                            .build();

        System.out.println(person);
    }
}

Output:

Person[name=Alice, age=30, [email protected]]

Explanation:

1. The Person record is defined with name, age, and email.

2. Inside the Person record, a static Builder class is defined.

3. The Builder class has methods name, age, and email for setting properties and a build method to create a Person instance.

4. In the main method, the Builder is used to construct a Person object with specified properties.

5. The program prints the details of the created Person object, demonstrating the Builder pattern with a Java record.

Comments