Java: Builder for Immutable POJO

PROBLEM

Let’s assume we have an immutable Person object:-

public final class Person implements Serializable {
    private final String name;
    private final Collection<Car> cars;

    public Person(final String name, final Collection<Car> cars) {
        this.name = name;
        this.cars = MoreObjects.firstNonNull(cars, ImmutableList.<Car>of());
    }

    public String getName() {
        return name;
    }

    public Collection<Car> getCars() {
        return cars;
    }
}

… and an immutable Car object:-

public final class Car implements Serializable {
    private final String license;
    private final LocalDate boughtOn;

    public Car(final String license, final LocalDate boughtOn) {
        this.license = license;
        this.boughtOn = boughtOn;
    }

    public String getLicense() {
        return license;
    }

    public LocalDate getBoughtOn() {
        return boughtOn;
    }
}

To create the Person object, we need to write something like this:-

final Person person = new Person("Mike",
                                 Collections.singletonList(
                                     new Car("123", new LocalDate(2015, 1, 1))));

If the POJO has a lot of properties, it becomes very difficult to keep track all the constructor argument positions.

SOLUTION

@mkarneim wrote a POJO Builder that creates the builder class(es) at compilation time.

To pull this off, we need to add the following dependency with provided scope.

<dependency>
    <groupId>net.karneim</groupId>
    <artifactId>pojobuilder</artifactId>
    <version>3.4.0</version>
    <scope>provided</scope>
</dependency>

Next, annotate with the Person constructor with @GeneratePojoBuilder. Make the constructor visibility to protected so that no one can directly instantiate that object.

public final class Person implements Serializable {
    private final String name;
    private final Collection<Car> cars;

    @GeneratePojoBuilder
    protected Person(final String name, final Collection<Car> cars) {
        this.name = name;
        this.cars = MoreObjects.firstNonNull(cars, ImmutableList.<Car>of());
    }

    public String getName() {
        return name;
    }

    public Collection<Car> getCars() {
        return cars;
    }
}

Do the same for Car object:-

public final class Car implements Serializable {
    private final String license;
    private final LocalDate boughtOn;

    @GeneratePojoBuilder
    protected Car(final String license, final LocalDate boughtOn) {
        this.license = license;
        this.boughtOn = boughtOn;
    }

    public String getLicense() {
        return license;
    }

    public LocalDate getBoughtOn() {
        return boughtOn;
    }

Next, and this is probably the most important step… run mvn clean compile to generate the builder classes. This allows the IDE to help us with code completion when using the builders.

Now, we can create the Person object like this:-

final Person person = new PersonBuilder()
        .withCars(ImmutableList.of(
                new CarBuilder()
                        .withBoughtOn(new LocalDate(2015, 1, 1))
                        .withLicense("123")
                        .build()))
        .withName("Mike")
        .build();

If you are running the program directly from IntelliJ, we need to ensure the program is compiled using Maven. To do this, edit the configuration for the runner class:-

Remove the default Make task:-

Add Maven goals:-

Enter clean and compile:-

Now, when IntelliJ runs the program, the program will be compiled using Maven to ensure no compilation errors on the builder classes during execution.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s