@Builder는 클래스에 대한 복잡한 빌더(complex builderAPI)를 생산한다.
다음 예제에서 @Builder를 사용하여 클래스를 인스턴스화 하는데 필요한 코드를 자동적으로 생산할 수 있다.
Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();
@Builder는 클래스, 생성자, 메서드에 선언할 수 있다. 보통 클래스 혹은 생성자에 선언하는 방식이 가장 공통적으로 사용되는 케이스 이지만 메서드 방식이 빌더 패턴 개념을 설명하는데 도움이 된다.
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set<String> occupations;
}
@Builder가 붙은 BuilderExample 이라는 클래스 이다. 하지만 실제 컴파일이 된다면 내부 클래스는 다음 예제와 같이 생성이 된다.(이름만 보여주기 위한 예제)
public static class BuilderExampleBuilder {
코드를 입력하세요........
}
정리 : 클래스에서 빌더 객체를 생성할 때는 builder() 메서드로 빌더 클래스의 인스턴스를 생성하고, 빌더 클래스에서는 build() 메서드로 실제 생성자를 호출하여 객체를 생성하고 있다.
Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();
@Builder를 사용할 때 다른 롬복의 어노테이션을 사용하여 생성된 요소(필드, 메서드)가 이미 클래스에 존재하여 중복이 발생된다면 자동으로 건너뛰어진다. 또한 @Builder가 붙은 클래스에는 @EqualsAndHashCode와 같이 메서드 혹은 생성자를 생성하는 롬복 어노테이션을 넣을 수 없다.
@Builder 어노테이션은 컬렉션 타입의 매개변수나 필드에 대해 singulr메서드를 제공한다. 이 메서드는 각 필드에 대한 setter메서드를 말한다. 다음 예제에서 컬렉션 타입에 대한 singulr메서드의 예제이다. 다음 예제는 Listjobs필드에 2개의 문자열이 추가가 된다.
Person.builder()
.job("Mythbusters")
.job("Unchained Reaction")
.build();
@Singular("singularItem")
private List<String> singularItem;
// 아래와 같은 방법으로 리스트에 추가 할수 있다.
BuilderVO.builder()
.singularItem("test1")
.singularItem("test2")
.singularItem("test3")
.build();
job필드에 여러개의 문자열을 추가하기 위해서는 해당 필드 위에 @Singular어노테이션을 달아야 한다. 이렇게 하면 롬복이 해당 필드를 처리하여 singular를 생성할 때 컬렉션에 여러 개의 요소를 삽입할 수 있게 된다
일반적인 생성자는 클래스의 필드 값을 한번에 모두 지정해야 하지만 @Builder 어노테이션을 사용하면 각 필드 값을 따로 설정할 수 있으며 원하는 필드만 주입하여 생성할 수 있다.
마지막으로 클래스에 @Builder를 적용하는 것은 클래스에 @AllArgsConstructor(access = AccessLevel.PACKAGE)를 추가하고 모든 생성자에 @Builder 어노테이션을 적용한것과 같다. 이는 직접 생성자를 작성하지 않은 경우에만 작동한다. 직접 생성자를 작성했다면 클래스 대신 생성자에 @Builder를 추가한다. 또한 @Value와 @Builder를 모두 적용하는 경우 @Builder가 생성하려고 하는 패키지 접근 수준의 생상자가 우선적으로 선택되어 @Value가 만들고자 하는 생성자를 억제한다.
정리 : @Builder는 @AllArgsConstructor(access = AccessLevel.PACKAGE)를 추가하며 w직접 생성자를 정의하는 경우 그 생성자에 @Builder를 붙여야만 작동한다. 또한 @Value와 @Builder가 모두 정의된 경우 @Builder어노테이션이 생성하는 @AllArgsConstructor(access = AccessLevel.PACKAGE)가 선택되며 @Value가 만드려고 하는 생성자는 억제 된다.
@Builder를 사용하여 자신의 클래스 인스턴스를 생성하는 builder를 생성할 경우 (자신의 타입을 반환하지 않는 메서드에 @Builder를 추가하는 경우 예외) @Builder(toBuilder = true)를 사용하여 toBulder()라는 인스턴스 메서드도 생성할 수 있다. 이 메서드는 현재 인스턴스의

toBuilder()는 @Builder(toBuilder=true)와 같이 속성을 붙일 수 있다.(default=false) 해당 속성을 지정하게 되면 toBuilder()를 사용할 수 있으며 해당 메서드는 builder로 생성한 인스턴스의 값을 복사하여 인스턴스를 새로 생성한다.
package com.example.youngJPA.member.temp;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@Builder(toBuilder = true)
@ToString
public class Order {
private String orderNumber;
private String productName;
private Long totalPrice;
}
package com.example.youngJPA.member.temp;
public class OrderMain {
public static void main(String[] args) {
Order order = Order.builder()
.orderNumber("order123")
.productName("something")
.totalPrice(1000L)
.build();
System.out.println("order: " + order);
Order.OrderBuilder orderBuilder = order.toBuilder();
System.out.println("orderBuilder" + orderBuilder);
Order updateOrder = order.toBuilder()
.totalPrice(20000L)
.build();
System.out.println("updateOrder: " + updateOrder);
}
}

order인스턴스를 builder()를 이용하여 생성한다.
orderBuilder 변수에 기존 order객체에서 toBuilder()를 사용하여 새로운 인스턴스를 생성하여 초기화 한다.
updateOrder변수에서는 toBuilder()를 사용하여 기존 객체인 order객체의 totalPrice필드의 값을 변경하여 다시 초기화 하였다.

toBuilde()를 사용하여 생성된 각각의 객체는 서로 다른 해쉬코드값을 반환한다.