lombok

Related POST

롬복 정리

1. Opening

이미 5년도 넘게 쓰고 있는 롬복을 세삼스레 정리하게 된 것은 이펙티브 자바와 JPA 공부덕이다
세삼스럽다.

2. 롬복이란

  • 롬복을 이용하면 특정 코드를 줄여서 가독성과 편의성을 올릴 수 있다
  • 특히 Getter와 Setter를 엄청나게 줄일 수 있어서 애용했었는데..
    오히려 그럼으로써 getter/setter를 남용하게 되는 원인이기도 하다

3. @Getter/@Setter

  • 게터메서드, 세터 메서드 생성
  • 필드가 아닌 클래스에 선언하는 경우 해당 클래스의 모든 필드의 게터,세터 생성
  • VO 클래스를 만들게 되는 경우 노가다에서 해방

4. 생성자

  • @NoArgsConstructor : 파라미터 없는 기본 생성자 생성
  • @AllArgsConstructor : 모든 필드 값을 파라미터로 받는 생성자
  • @RequiredArgsConstructor
    • final, @NonNull 인 필드값만 파라미터로 받는 생성
    • 사실 서비스나 컨트롤러도 이걸로 생성하는게 가장 깔끔하다
      • 어차피 컨트롤러는 서비스, 서비스는 JPA리포지토리등을 주입받음
      • 중간에 빈을 바꿀 일은 없음 : 처음 빈이 로딩될때 전부 설정값 반영됨
      • 그러면 해당 빈을 final로 하고 이 어노테이션을 사용하면 생성자 주입 방식 사용됨
  • @NonNull : 메서드 파라미터에 사용, null이 파라미터로 전달되면 예외
  • access 필드로 접근 권한 수정 가능
  • @NoArgsConstructor(access = AccessLevel.PROTECTED)
    • JPA 기본 스펙상 기본 생성자 필요
    • protected로 제어하는 것까지 허용되며 new로 객체생성함을 막을 수 있음

5. @ToString

  • 이클립스나 인텔리제이가 만들어주는 ToString을 자동으로 생성
  • 롬복을 사용하기전에는 Apache Commons Lang 라이브러리의 ToStringBuilder를 사용
  • 보통 문제가 되는게 JPA 양방향 관계에서 잘못 사용하면 무한 순환해서 스택이 터지는 오류
  • 그래서 개인적으로 exclude로 특정 필드 제외하는 것을 애용 했음
  • 공부를 해보니 of를 권장 : exclude보다 새로 객체를 추가해 줄 떄 마다 수정 비용이 적다고 한다

6. @EqualsAndHashCode

  • equals(), hashcode()를 자동으로 생성해줌
  • ToString,처럼 exclude, callSuper 사용 가능
  • 객체 직렬화에서 제외할때 사용하는 transient 키워드 사용시 exclude 효과
  • callSuper : 해당 속성을 통해 부모 클래스 필드까지 동일한지 체크 가능
  • 성능 이슈가 발생할 수 있다고 한다(Set 자료 구조)

7. @Data

  • @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode를 한번에
  • 사실 조금만 공부하면 실무에서는 절대 사용하지 않아야 한다.

8. @Builder

  • GoF 빌더 패턴을 간단하게 만들 수 있다
  • 클래스에 설정시 @AllArgsConstructor효과가 생기므로 기피
  • 생성자 빌더 패턴 적용시 장점
    • 필요한 데이터만 설정 가능
      • 일반 생성자 사용시 필요없는 값에 더미값을 넣거나 해당 값이 없는 생성자를 새로 적용 필요
      • 빌더 사용시 동적으로 처리 : 객체 생성 용이, 코드양 줄임
    • 유연성 확보
      • 파라미터 추가시 기존 코드 수정 필요. 생성 코드가 100개면 100개에 따른 수정이 필요
      • 빌더 패턴 이용시 기존 코드에 영향을 주지 않음
    • 가독성 확보
      • 파라미터 증가시 리딩 효율 급격히 떨어짐
      • 빌더 패턴 적용시 가독성 유지, 직관적으로 어떤 데이터 설정인지 파악 가능
    • 불변성 확보
      • Setter 사용은 불필요한 확장 가능성
        • Open-Closed 위배
        • 불필요한 코드리딩 필요
      • 따라서 클래스 변수를 final로 선언하고 객체의 생성은 빌더에 맡겨야 한다
  • advance
    • builderClassName 파라미터로 nested 빌더 클래스의 이름을 (클래스명Builder가 기본)
    • builderMethodName으로 빌더 클래스를 반환하는 static 메소드의 이름을 (builder()가 기본)
    • buildMethodName으로 객체를 반환하는 빌드 메소드의 이름 (build()가 기본)을 설정할 수 있다
    • @Builder.Default : 기본값 설정, 해당 값이 없으면 정해진 기본값을 토태로 빌더가 실행된다
    • @Singular : List등의 컬렉션에 사용
      • builder에서 값을 추가할 떄 사용되는 메서드 이름 입력
      • 빈 컬렉션이 자동으로 생성되며, 이 컬렉션은 수정 불가
    • @Builder(toBuilder = true)
      • 인스턴스에서도 builder 가능.
      • 해당 인스턴스에서 일부 값만 변경한 새로운 객체 생성 가능

9. @Cleanup

  • try-with-resource구문과 비슷한 효과
  • 차이점
    • try-with-resource : 구문이 종료될 때 AutoCloseable인터페이스의 close()호출
    • @Cleanup : scope 종료시 어노테이션이 설정된 변수의 close() 호출

10. val

  • 스칼라의 val 키워드 기능
  • 객체 타입을 추론한 불변 값 선언
  • 스클라와 달리 지역변수, foreach 구문에만 사용 가능
  • 메소드 파라미터, 클래스 필드에 사용 불가

11. @Value

  • @Data어노테이션과 비슷하지만 필드를 변경할 수 없는 불변 객체를 만듬
  • 예시
    1
    2
    3
    4
    5
    6
    @Value
    public class TestValve {
    String name;
    int val1;
    int val2;
    }
  • 빌드되면 다음과 같아진다
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    public final class TestValve {
    private final String name;
    private final int val1;
    private final int val2;

    public TestValve(final String name, final int val1, final int val2) {
    this.name = name;
    this.val1 = val1;
    this.val2 = val2;
    }

    public String getName() {
    return this.name;
    }

    public int getVal1() {
    return this.val1;
    }

    public int getVal2() {
    return this.val2;
    }

    public boolean equals(final Object o) {
    if (o == this) {
    return true;
    } else if (!(o instanceof TestValve)) {
    return false;
    } else {
    TestValve other = (TestValve)o;
    if (this.getVal1() != other.getVal1()) {
    return false;
    } else if (this.getVal2() != other.getVal2()) {
    return false;
    } else {
    Object this$name = this.getName();
    Object other$name = other.getName();
    if (this$name == null) {
    if (other$name != null) {
    return false;
    }
    } else if (!this$name.equals(other$name)) {
    return false;
    }

    return true;
    }
    }
    }

    public int hashCode() {
    int PRIME = true;
    int result = 1;
    int result = result * 59 + this.getVal1();
    result = result * 59 + this.getVal2();
    Object $name = this.getName();
    result = result * 59 + ($name == null ? 43 : $name.hashCode());
    return result;
    }

    public String toString() {
    String var10000 = this.getName();
    return "TestValve(name=" + var10000 + ", val1=" + this.getVal1() + ", val2=" + this.getVal2() + ")";
    }
    }
  • @Value는 val 언노테이션 사용 → val 어노테이션 사용 가능할 때만 @Value 사용 가능

12 @Log

  • 클래스 상단의 static final log 필드 자동 생성
  • SLF4J 사용하는 경우 @Slf4j를 사용하면 된다.

13. @Synchronized

  • Synchronized보다 더 자세한 설정이 가능한 어노테이션
  • Synchronized : static, insttance 단위로 락
  • @Synchronized: @파라미터로 입력받는 오브젝트 단위
  • 파라미터가 없는 경우 어노테이션이 사용된 메소드 단위 락
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class TestValve {
    public static final Object OBJECT_LOCK = new Object();
    String name;
    String name2;
    int value;

    //파라미터로 입력받는 Object 단위 Lock
    @Synchronized("OBJECT_LOCK")
    public int valCalc(int value2){
    return value + value2;
    }

    @Synchronized("OBJECT_LOCK")
    public int getValue() {
    return value;
    }

    @Synchronized
    public String getName() {
    return name;
    }

    @Synchronized
    public String getName2() {
    return name2;
    }
    }

롬복 사용시 부모 생성자 호출 불가

일반적인 경우

  • 생성자에 builder 어노테이션을 추가하면 super 가능
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Getter
    @AllArgsConstructor
    public class Parent {
    private final String parentName;
    private final int parentAge;
    }
    @Getter
    public class Child extends Parent {
    private final String childName;
    private final int childAge;
    @Builder
    public Child(String parentName, int parentAge, String childName, int childAge) {
    super(parentName, parentAge);
    this.childName = childName;
    this.childAge = childAge;
    }
    }

답변 갱신

  • @SuperBuilder을 사용해서 상속되는 클래스 빌더 생성 가능
  • Lombok 1.18.2 이후부터 사용 가능
공유하기