diff --git a/patterns/design-patterns-2/pom.xml b/patterns/design-patterns-2/pom.xml
index 392035ab84..17afff87ae 100644
--- a/patterns/design-patterns-2/pom.xml
+++ b/patterns/design-patterns-2/pom.xml
@@ -30,6 +30,17 @@
org.apache.commons
commons-lang3
${commons-lang3.version}
+
+
+ org.inferred
+ freebuilder
+ ${freebuilder.version}
+ true
+
+
+ com.google.code.findbugs
+ jsr305
+ ${javax.annotations.version}
@@ -38,5 +49,7 @@
1.8
1.8
16.0.2
+ 2.4.1
+ 3.0.2
diff --git a/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/Address.java b/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/Address.java
new file mode 100644
index 0000000000..6d727f6d9f
--- /dev/null
+++ b/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/Address.java
@@ -0,0 +1,25 @@
+package com.baeldung.freebuilder;
+
+import org.inferred.freebuilder.FreeBuilder;
+
+import java.util.Optional;
+
+@FreeBuilder
+public interface Address {
+
+ Optional getAddressLine1();
+
+ Optional getAddressLine2();
+
+ Optional getAddressLine3();
+
+ String getCity();
+
+ Optional getState();
+
+ Optional getPinCode();
+
+ class Builder extends Address_Builder {
+
+ }
+}
\ No newline at end of file
diff --git a/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/Employee.java b/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/Employee.java
new file mode 100644
index 0000000000..06558dde0b
--- /dev/null
+++ b/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/Employee.java
@@ -0,0 +1,65 @@
+package com.baeldung.freebuilder;
+
+import org.inferred.freebuilder.FreeBuilder;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+@FreeBuilder
+public interface Employee {
+
+ String getName();
+
+ int getAge();
+
+ String getDepartment();
+
+ String getRole();
+
+ String getSupervisorName();
+
+ String getDesignation();
+
+ String getEmail();
+
+ long getPhoneNumber();
+
+ Optional getPermanent();
+
+ Optional getDateOfJoining();
+
+ @Nullable
+ String getCurrentProject();
+
+ Address getAddress();
+
+ List getAccessTokens();
+
+ Map getAssetsSerialIdMapping();
+
+ Optional getSalaryInUSD();
+
+
+ class Builder extends Employee_Builder {
+
+ public Builder() {
+ // setting default value for department
+ setDepartment("Builder Pattern");
+ }
+
+ @Override
+ public Builder setEmail(String email) {
+ if (checkValidEmail(email))
+ return super.setEmail(email);
+ else
+ throw new IllegalArgumentException("Invalid email");
+
+ }
+
+ private boolean checkValidEmail(String email) {
+ return email.contains("@");
+ }
+ }
+}
diff --git a/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/builder/Employee.java b/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/builder/Employee.java
new file mode 100644
index 0000000000..141d734c43
--- /dev/null
+++ b/patterns/design-patterns-2/src/main/java/com/baeldung/freebuilder/builder/Employee.java
@@ -0,0 +1,53 @@
+package com.baeldung.freebuilder.builder;
+
+public class Employee {
+
+ private final String name;
+ private final int age;
+ private final String department;
+
+ private Employee(String name, int age, String department) {
+ this.name = name;
+ this.age = age;
+ this.department = department;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public String getDepartment() {
+ return department;
+ }
+
+ public static class Builder {
+
+ private String name;
+ private int age;
+ private String department;
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setAge(int age) {
+ this.age = age;
+ return this;
+ }
+
+ public Builder setDepartment(String department) {
+ this.department = department;
+ return this;
+ }
+
+ public Employee build() {
+ return new Employee(name, age, department);
+ }
+ }
+
+}
diff --git a/patterns/design-patterns-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java b/patterns/design-patterns-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java
new file mode 100644
index 0000000000..d28edfcf3d
--- /dev/null
+++ b/patterns/design-patterns-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java
@@ -0,0 +1,226 @@
+package com.baeldung.freebuilder;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class EmployeeBuilderUnitTest {
+
+ private static final int PIN_CODE = 223344;
+ public static final String CITY_NAME = "New York";
+ public static final int INPUT_SALARY_EUROS = 10000;
+ public static final double EUROS_TO_USD_RATIO = 0.6;
+
+ @Test
+ public void whenBuildEmployeeWithAddress_thenReturnEmployeeWithValidAddress() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).build();
+
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setPermanent(true)
+ .setRole("developer")
+ .setAddress(address)
+ .build();
+
+ // then
+ assertTrue(employee.getAddress().getCity().equalsIgnoreCase(CITY_NAME));
+
+ }
+
+ @Test
+ public void whenMapSalary_thenReturnEmployeeWithSalaryInUSD() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).setPinCode(PIN_CODE).build();
+
+ long salaryInEuros = INPUT_SALARY_EUROS;
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder
+ .setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setPermanent(true)
+ .setRole("developer")
+ .setAddress(address)
+ .mapSalaryInUSD(sal -> salaryInEuros * EUROS_TO_USD_RATIO)
+ .build();
+
+ // then
+ assertTrue(employee.getAddress().getPinCode().get() == PIN_CODE);
+
+ }
+
+ @Test
+ public void whenOptionalFields_thenReturnEmployeeWithEmptyValues() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).build();
+
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setPermanent(true)
+ .setRole("developer")
+ .setAddress(address)
+ .build();
+
+ // then
+ assertTrue(employee.getPermanent().isPresent());
+ assertTrue(employee.getPermanent().get());
+ assertFalse(employee.getDateOfJoining().isPresent());
+
+ }
+
+ @Test
+ public void whenNullableFields_thenReturnEmployeeWithNullValueForField() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).build();
+
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setNullablePermanent(null)
+ .setDateOfJoining(Optional.empty())
+ .setRole("developer")
+ .setAddress(address)
+ .build();
+
+ // then
+ assertNull(employee.getCurrentProject());
+
+ }
+
+ @Test
+ public void whenCollectionFields_thenReturnEmployeeWithValues() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).build();
+
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setNullablePermanent(null)
+ .setDateOfJoining(Optional.empty())
+ .setRole("developer")
+ .addAccessTokens(1221819L)
+ .addAccessTokens(1223441L, 134567L)
+ .setAddress(address)
+ .build();
+
+ // then
+ assertTrue(employee.getAccessTokens().size() == 3);
+
+ }
+
+ @Test
+ public void whenMapFields_thenReturnEmployeeWithValues() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).build();
+
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setNullablePermanent(null)
+ .setDateOfJoining(Optional.empty())
+ .setRole("developer")
+ .addAccessTokens(1221819L)
+ .addAccessTokens(1223441L, 134567L)
+ .putAssetsSerialIdMapping("Laptop", 12345L)
+ .setAddress(address)
+ .build();
+
+ // then
+ assertTrue(employee.getAssetsSerialIdMapping().size() == 1);
+
+ }
+
+ @Test
+ public void whenNestedBuilderTypes_thenReturnEmployeeWithValues() {
+
+ // when
+ Address.Builder addressBuilder = new Address.Builder();
+ Address address = addressBuilder.setCity(CITY_NAME).build();
+
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setDesignation("author")
+ .setEmail("abc@xyz.com")
+ .setSupervisorName("Admin")
+ .setPhoneNumber(4445566)
+ .setNullablePermanent(null)
+ .setDateOfJoining(Optional.empty())
+ .setRole("developer")
+ .addAccessTokens(1221819L)
+ .addAccessTokens(1223441L, 134567L)
+ .putAssetsSerialIdMapping("Laptop", 12345L)
+ .setAddress(address)
+ .mutateAddress(a -> a.setPinCode(112200))
+ .build();
+
+ // then
+ assertTrue(employee.getAssetsSerialIdMapping().size() == 1);
+
+ }
+
+ @Test()
+ public void whenPartialEmployeeWithValidEmail_thenReturnEmployeeWithEmail() {
+
+ // when
+ Employee.Builder builder = new Employee.Builder();
+
+ Employee employee = builder.setName("baeldung")
+ .setAge(10)
+ .setEmail("abc@xyz.com")
+ .buildPartial();
+
+ assertNotNull(employee.getEmail());
+ }
+
+}
diff --git a/patterns/design-patterns-2/src/test/java/com/baeldung/freebuilder/builder/EmployeeBuilderUnitTest.java b/patterns/design-patterns-2/src/test/java/com/baeldung/freebuilder/builder/EmployeeBuilderUnitTest.java
new file mode 100644
index 0000000000..1394f851b6
--- /dev/null
+++ b/patterns/design-patterns-2/src/test/java/com/baeldung/freebuilder/builder/EmployeeBuilderUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.freebuilder.builder;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+
+class EmployeeBuilderUnitTest {
+
+ public static final String NAME = "baeldung";
+
+ @Test
+ public void whenBuildEmployee_thenReturnValidEmployee() {
+
+ // when
+ Employee.Builder emplBuilder = new Employee.Builder();
+
+ Employee employee = emplBuilder
+ .setName(NAME)
+ .setAge(12)
+ .setDepartment("Builder Pattern")
+ .build();
+
+ //then
+ Assertions.assertTrue(employee.getName().equalsIgnoreCase(NAME));
+ }
+
+}
\ No newline at end of file