A Generic Builder Pattern for Simplifying Java Object Creation
This article demonstrates how to replace verbose Java object instantiation and setter calls with a reusable generic Builder that supports chained property setting via functional interfaces, showing both a simple example and a full implementation supporting up to three parameters per setter.
Java developers often face the "do you have a girlfriend?" metaphor, where creating an object with many attributes becomes cumbersome using the traditional new operator followed by multiple setter calls.
First, a naive GirlFriend class is shown with a few fields and a main method that creates an instance and sets its properties one by one:
public class GirlFriend {
private String name;
private int age;
// ... getters & setters omitted
public static void main(String[] args) {
GirlFriend myGirlFriend = new GirlFriend();
myGirlFriend.setName("小美");
myGirlFriend.setAge(18);
}
}When the class grows with many more attributes (bust, waist, hips, hobby list, birthday, address, mobile, email, hairColor, gift map, etc.), the instantiation and property‑setting code becomes long and hard to maintain.
public class GirlFriend {
private String name;
private int age;
private int bust;
private int waist;
private int hips;
private List<String> hobby;
private String birthday;
private String address;
private String mobile;
private String email;
private String hairColor;
private Map<String, String> gift;
// ... getters & setters omitted
public static void main(String[] args) {
GirlFriend myGirlFriend = new GirlFriend();
myGirlFriend.setName("小美");
myGirlFriend.setAge(18);
myGirlFriend.setBust(33);
myGirlFriend.setWaist(23);
myGirlFriend.setHips(33);
myGirlFriend.setBirthday("2001-10-26");
myGirlFriend.setAddress("上海浦东");
myGirlFriend.setMobile("18688888888");
myGirlFriend.setEmail("[email protected]");
myGirlFriend.setHairColor("浅棕色带点微卷");
// ... more setters
}
}The drawbacks are obvious: separate instantiation and property assignment, repetitive code, and poor readability.
To solve this, the article introduces a universal generic Builder that works for any class without modifying the original class or requiring Lombok.
Usage example with the same GirlFriend class:
public class GirlFriend {
// ... properties omitted
public void addHobby(String hobby) { /* add hobby */ }
public void addGift(String day, String gift) { /* add gift */ }
public void setVitalStatistics(int bust, int waist, int hips) { /* set three stats */ }
public static void main(String[] args) {
GirlFriend myGirlFriend = Builder.of(GirlFriend::new)
.with(GirlFriend::setName, "小美")
.with(GirlFriend::setAge, 18)
.with(GirlFriend::setVitalStatistics, 33, 23, 33)
.with(GirlFriend::setBirthday, "2001-10-26")
.with(GirlFriend::setAddress, "上海浦东")
.with(GirlFriend::setMobile, "18688888888")
.with(GirlFriend::setEmail, "[email protected]")
.with(GirlFriend::setHairColor, "浅棕色带点微卷")
.with(GirlFriend::addHobby, "逛街")
.with(GirlFriend::addHobby, "购物")
.with(GirlFriend::addHobby, "买东西")
.with(GirlFriend::addGift, "情人节礼物", "LBR 1912女王时代")
.with(GirlFriend::addGift, "生日礼物", "迪奥烈焰蓝金")
.with(GirlFriend::addGift, "纪念日礼物", "阿玛尼红管唇釉")
.build();
}
}The generic Builder class is implemented as follows, supporting up to three parameters per setter via functional interfaces:
/**
* Generic Builder implementation
*/
public class Builder
{
private final Supplier
instantiator;
private List
> modifiers = new ArrayList<>();
public Builder(Supplier
instantiator) {
this.instantiator = instantiator;
}
public static
Builder
of(Supplier
instantiator) {
return new Builder<>(instantiator);
}
public
Builder
with(Consumer1
consumer, P1 p1) {
Consumer
c = instance -> consumer.accept(instance, p1);
modifiers.add(c);
return this;
}
public
Builder
with(Consumer2
consumer, P1 p1, P2 p2) {
Consumer
c = instance -> consumer.accept(instance, p1, p2);
modifiers.add(c);
return this;
}
public
Builder
with(Consumer3
consumer, P1 p1, P2 p2, P3 p3) {
Consumer
c = instance -> consumer.accept(instance, p1, p2, p3);
modifiers.add(c);
return this;
}
public T build() {
T value = instantiator.get();
modifiers.forEach(modifier -> modifier.accept(value));
modifiers.clear();
return value;
}
@FunctionalInterface
public interface Consumer1
{ void accept(T t, P1 p1); }
@FunctionalInterface
public interface Consumer2
{ void accept(T t, P1 p1, P2 p2); }
@FunctionalInterface
public interface Consumer3
{ void accept(T t, P1 p1, P2 p2, P3 p3); }
}This implementation allows easy extension to more parameters by adding additional functional interfaces. The article concludes by encouraging readers to apply the Builder to their own objects.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.