롬복의 @Builder 공식문서 정리

이동영·2024년 3월 19일

자바 개념정리

목록 보기
20/21

@Builder는 클래스에 대한 복잡한 빌더(complex builderAPI)를 생산한다.

  • complex는 많은 필드나 속성을 가진 클래스를 말한다.
  • builder API는 객체를 생성하는 과정을 유연하게 하고 읽기 쉽게 만드는 방식이다.

다음 예제에서 @Builder를 사용하여 클래스를 인스턴스화 하는데 필요한 코드를 자동적으로 생산할 수 있다.

Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();

@Builder는 클래스, 생성자, 메서드에 선언할 수 있다. 보통 클래스 혹은 생성자에 선언하는 방식이 가장 공통적으로 사용되는 케이스 이지만 메서드 방식이 빌더 패턴 개념을 설명하는데 도움이 된다.

  • @Builder어노테이션은 Java코드에서 객체 생성을 간소화 하고 유연하게 만드는 Project Lombok 라이브러리의 기능이며 7가지의 코드가 자동적으로 생성이 된다.

1. 내부 정적 클래스(Inner Static Class): FooBuilder

  • FooBuilder라는 이름의 내부 정적 클래스가 생성이 된다.
  • 이 클래스는 외부 클래스에 정의된 내부 클래스이며 다른 패키지 코드에서 직접적으로 접근할 수 없다.
  • 클래스의 이름은 @Builder가 붙은 클래스 이름뒤에 Builder가 붙는다. 예를들어 Person클래스라면 PersonBuilder라는 내부 클래스가 생성이 된다. 다음 예제는 이 설명에 대한 예제이다.
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 {
  코드를 입력하세요........
  }
  • 이 클래스는 외부 클래스와 동일한 타입의 인자를 가지고 생성이 된다.
  • 필드 : 외부 클래스의 각 파라미터에 해당하는 non static non final필드가 포함된다.
  • 생성자 : package private이며 인자가 없는 no-args 즉 기본 생성자가 포함된다.
  • setter : 빌더 안에는 외부 클래스의 필드와 동일한 이름으로 체인 형식의 setter를 제공할 수 있도록 구현이 되어있다.
  • 빌더 안에는 각 필드 값을 받아서 완성된 객체를 반환하는 build()를 지원한다. 즉 값을 설정하고 build()를 사용하면 설정한 값의 대한 객체를 반환한다.

정리 : 클래스에서 빌더 객체를 생성할 때는 builder() 메서드로 빌더 클래스의 인스턴스를 생성하고, 빌더 클래스에서는 build() 메서드로 실제 생성자를 호출하여 객체를 생성하고 있다.

Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();
  • 빌더는 toString()을 자동으로 구현한다.
  • builder()는 빌더의 새 인스턴스를 생성한다. 즉 builder()를 호출하면 인스턴스가 반환이 되며 이때 각 필드에 대한 setter와 유사한 메소드로 값을 설정한뒤 build()를 사용하면 최종 객체가 완성되어 반환된다.

@Builder를 사용할 때 다른 롬복의 어노테이션을 사용하여 생성된 요소(필드, 메서드)가 이미 클래스에 존재하여 중복이 발생된다면 자동으로 건너뛰어진다. 또한 @Builder가 붙은 클래스에는 @EqualsAndHashCode와 같이 메서드 혹은 생성자를 생성하는 롬복 어노테이션을 넣을 수 없다.

singulr메서드를 사용하여 컬렉션에 요소 추가

@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()

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()를 사용하여 생성된 각각의 객체는 서로 다른 해쉬코드값을 반환한다.

참조 : https://wildeveloperetrain.tistory.com/298

profile
가치를 제공하는 개발자

0개의 댓글