Lombok 주의사항

22·2023년 12월 13일

Java

목록 보기
3/3

Lombok 은 보일러 플레이트 코드를 감소시켜 코드 양을 줄일 수 있는 Java 의 강력한 라이브러리이다.
하지만 자동으로 해주는 것이 많은 만큼 주의 사항도 있다.

@AllArgsConstructor @RequiredArgsConstructor

  • 두 어노테이션은 어노테이션 선언 하나로 편리하게 생성자를 만들어주는 기능이다.
  • 치명적인 문제가 한 가지 있다. 바로 선언된 필드의 순서가 바뀌면 자동으로 생성자의 순서도 바뀐다. 두 필드의 타입이 같은 경우 순서가 바뀌어도 에러가 발생하지 않기 때문에 알 수 없다.
@AllArgsConstructor
public class Square { // 두 필드의 순서가 바뀌면, 생성자의 순서도 바뀐다.
    private int width;
    private int height;
}
  • 위의 치명적인 이유 때문에 두 어노테이션은 지양해야 한다.



@Builder

  • 위에 두 어노테이션 대신 Builder 패턴을 쉽게 사용할 수 있는 @Builder 사용을 추천하기도 한다.

  • @Builder 는 클래스와 생성자에 어노테이션을 사용할 수 있다. 클래스에 사용하는 경우 @AllArgsConstructor 를 사용하기 때문에 지양해야한다.

    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. This only works if you haven't written any explicit constructors yourself. If you do have an explicit constructor, put the @Builder annotation on the constructor instead of on the class. Note that if you put both @Value and @Builder on a class, the package-private constructor that @Builder wants to generate 'wins' and suppresses the constructor that @Value wants to make.
    https://projectlombok.org/features/Builder

  • 메서드에 사용하는 경우 또한 주의해야한다. @Builder 를 사용해서 생성자를 사용하는 경우 실수로 필드를 세팅하지 않는 경우, 의도치 않은 null 이 들어갈 수 있다. (클래스도 같은 문제가 있다.)

@Getter
public class Person {

    private final String lastName;
    private final String firstName;

    @Builder
    public Person(String lastName, String firstName) {
        this.lastName = lastName;
        this.firstName = firstName;
    }
}
Person kim = Person.builder()
                .firstName("민수")
                .build();

        System.out.println(kim.getFirstName());
        System.out.println(kim.getLastName());
        
> 민수
> null
  • private final 로 선언한 두 필드 중 lastName 에 값을 세팅하지 않으면 null 이 출력된다. 아래 공식 문서에 설명대로 세팅되지 않은 필드는 0 / null / false 로 세팅된다.

If a certain field/parameter is never set during a build session, then it always gets 0 / null / false. If you've put @Builder on a class (and not a method or constructor) you can instead specify the default directly on the field, and annotate the field with @Builder.Default:
@Builder.Default private final long created = System.currentTimeMillis();
https://projectlombok.org/features/Builder

  • @Builder 가 클래스에 있고, 필드에 컬렉션이 있는 경우에도 주의사항이 있다.
@Getter
@Builder
public class Person {

    private final String lastName;
    private final String firstName;
    private List<String> cars = new ArrayList<>();
}
Person kim = Person.builder()
               		.firstName("김")
                	.build();
        
System.out.println(kim.getCars());
> null
  • 초기화가 됐다고 생각하지만, null 이 리턴된다. @Builder 를 사용할 때 컬렉션에 @Singular 를 선언하지 않으면 null 이 리턴된다. @Singular 를 사용해야 빈 리스트가 할당된다.
  • 이런 경우 @Builder.Default, @Signular, private final 셋 중 하나를 컬렉션에 선언하면 초기화 된다.


참고

https://projectlombok.org/features/Builder
https://johngrib.github.io/wiki/pattern/builder/#fn:lombok-builder
https://kwonnam.pe.kr/wiki/java/lombok/pitfall
https://velog.io/@nawhew/%EB%A1%AC%EB%B3%B5-Builder-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98%EC%97%90-null%EC%9D%B4-%EC%84%B8%ED%8C%85%EB%90%98%EB%8A%94-%EA%B2%BD%EC%9A%B0
https://stackoverflow.com/questions/32824821/lombok-builder-not-initializing-collections

0개의 댓글