matsev
matsev

Reputation: 33789

How to create a builder for third party POJO?

How can I create a Lombok builder for a third party class (i.e. I cannot modify its source code)?

I have an existing class that I cannot change:

public class ThirdPartyPojo {
    // one of many properties
    private String name;

    public ThirdPartyPojo() {
        // default no-args constructor
    }

    String getName() {
        return this.name;
    }

    void setName(String name) {
        this.name = name;
    }

    // many more getters and setters
}

Now I would like to create a @Builder so that I get a fluent builder API to simplify instantiation of ThirdPartyPojo with default values.

This is what I tried:

@Builder
public class ThirdPartyPojoBuilder extends ThirdPartyPojo {

    @Default
    private String name = "default name";

    // many more default values for other properties
}

The code compiles and I am able to reference the builder, e.g.

ThirdPartyPojo pojoWithDefaultName = ThirdPartyPojoBuilder.builder().build();
ThirdPartyPojo pojoWithCustomName = ThirdPartyPojoBuilder.builder().name("custom name").build();

System.out.println(pojoWithDefaultName.getName());
System.out.println(pojoWithCustomName.getName());

However, this does not work as getName() returns null for both pojoWithDefaultName and pojoWithCustomName.

Upvotes: 6

Views: 1942

Answers (2)

matsev
matsev

Reputation: 33789

Based on pirho's answer, I found a feasible solution that also honors default values.

  1. Extend the third party class
  2. Add the @Builder annotation on class level
  3. Add any fields that should be configurable by the builder
  4. Add @Default and assign values to any fields that should have a default value
  5. Add a constructor with all parameters matching all fields
  6. Add this.set*(...) for each constructor parameter

e.g.


@Builder
public class ThirdPartyPojoBuilder extends ThirdPartyPojo {
    @Default
    private String name = "default name" 

    public ThirdPartyPojoBuilder(String name) {
        this.setName(name);
    }
}

which also supports default values:

ThirdPartyPojo pojoWithDefaultName = ThirdPartyPojoBuilder.builder().build();
ThirdPartyPojo pojoWithCustomName = ThirdPartyPojoBuilder.builder().name("custom name").build();

System.out.println(pojoWithDefaultName.getName()); // prints "default name"
System.out.println(pojoWithCustomName.getName());  // prints "custom name"

Upvotes: 3

pirho
pirho

Reputation: 12245

I am afraid there is no easy way out to have Lombok automagically generate builder the way you might want. What Lombok @Builder does in general is:

Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor.

So that is - I think - all that you can do:

public class ThirdPartyPojoBuilder extends ThirdPartyPojo {
    @Builder
    public ThirdPartyPojoBuilder(String name) {
        this.setName(name);
    }
}

Then it is possible - for example:

var thirdPartyPojo = ThirdPartyPojoBuilder.builder().name("A Name").build();

Upvotes: 2

Related Questions