Lombok 활용하기

Gradle 설정

configurations {
    compileOnly {
        extendsFrom annotationProcessor
  }
}

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.20'
    annotationProcessor 'org.projectlombok:lombok:1.18.20'

    testCompileOnly 'org.projectlombok:lombok:1.18.20'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'
}

추가후에 반드시 아래 설정을 해줘야한다.

  • Preferences -> Compiler -> Annotation Processors

Enable annotation processing을 반드시 설정해줘야한다.

@Getter / @Setter

접근자(getXxx())와 설정자(setXxx())를 자동으로 생성해준다.

package dh0023.springcore.member.domain;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Member {

    private Long id;
    private String name;
    private Grade grade;

}

다음과 같이 자동으로 getter와 setter가 생성된 것을 확인할 수 있다.

생성자 자동 생성

@NoArgsConstructor

파라미터가 없는 생성자를 자동으로 생성해준다.

@AllArgsConstructor

모든 필드 값을 파라미터로 받는 생성자를 생성해준다.

@NoArgsConstructor
@AllArgsConstructor
public class Member {

    private Long id;
    private String name;
    private Grade grade;

}

@RequiredArgsConstructor

@RequiredArgsConstructor 어노테이션은 final이나 @NonNull인 필드 값만 파라미터로 받는 생성자를 만들어준다.

이렇게 생성하면, 코드가 훨씬 깔끔해지고 간편한 것을 확인할 수 있다.

@Component
public class OrderServiceImpl implements OrderService{

    // final은 반드시 값이 있어야한다.
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

위와 같이 생성자를 직접 만들어줄 필요 없이 해당 어노테이션만 별도로 설정해주면 된다.

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{

    // final은 반드시 값이 있어야한다.
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}

intelliJ에서 command + F12로 해당 클래스의 메서드와 변수를 확인할 수 있는데, 아래와 같이 생성자가 생성된 것을 확인할 수 있다.

@ToString

toString() 메소드도 lombok의 @ToString 어노테이션으로 간단하게 생성할 수 있다. 또한, exclude 옵션으로 특정 필드를 제외할 수 있다.

@Getter
@Setter
@ToString(exclude = "grade")
@AllArgsConstructor
public class Member {

    private Long id;
    private String name;
    private Grade grade;

}
public class LombokTest {
    @Test
    @DisplayName("toString() 테스트")
    void getToString() {
        Member member = new Member(1L, "test123", Grade.VIP);

        System.out.println(member);
    }
}
Member(id=1, name=test123)

Process finished with exit code 0

exclude 에 포함된 필드를 제외하고 전체 출력되는 것을 확인할 수 있다.

@Builder

@Builder 로 단순하게 빌더를 생성할 수 있다.

public class Item {
    private final String itemCd; // 필수
    private final String itemNm; // 필수
    private final String ctgId;  // 필수
    private final BigDecimal price; // 선택
    private final String sellTypeCd; // 선택

    public static class Builder {
      private final String itemCd; // 필수
        private final String itemNm; // 필수
      private final String ctgId;  // 필수

      // 선택적 매개변수는 default 값으로 초기화
      private BigDecimal price = BigDecimal.ZERO; 
      private String sellTypeCd = "00";

      public Builder(String itemCd, String itemNm, String ctgId){
        this.itemCd = itemCd;
        this.itemNm = itemNm;
        this.ctgId = ctgId;
      }
      public Builder price(BigDecimal price){
        this.price = price;
        return this;
      }

      public Builder sellTypeCd(String sellTypeCd){
        this.sellTypeCd = sellTypeCd;
        return this;
      }

      public Item build(){
        return new Item(this);
      }
    }

      private Item(Builder builder){
      itemCd = builder.itemCd;
      itemNm = builder.itemNm;
      ctgId = builder.ctgId;
      price = builder.price;
      sellTypeCd = builder.sellTypeCd;
    } 
}

다음과 같이 Item.Builder를 단순히 @Builder 어노테이션 만으로 구현할 수 있다.

@Builder
public class Item {
    private final String itemCd; // 필수
    private final String itemNm; // 필수
    private final String ctgId;  // 필수
    private final BigDecimal price; // 선택
    private final String sellTypeCd; // 선택
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package ch2.dahye.item2;

import java.math.BigDecimal;

public class Item {
    private final String itemCd;
    private final String itemNm;
    private final String ctgId;
    private final BigDecimal price;
    private final String sellTypeCd;

    Item(String itemCd, String itemNm, String ctgId, BigDecimal price, String sellTypeCd) {
        this.itemCd = itemCd;
        this.itemNm = itemNm;
        this.ctgId = ctgId;
        this.price = price;
        this.sellTypeCd = sellTypeCd;
    }

    public static Item.ItemBuilder builder() {
        return new Item.ItemBuilder();
    }

    public static class ItemBuilder {
        private String itemCd;
        private String itemNm;
        private String ctgId;
        private BigDecimal price;
        private String sellTypeCd;

        ItemBuilder() {
        }

        public Item.ItemBuilder itemCd(String itemCd) {
            this.itemCd = itemCd;
            return this;
        }

        public Item.ItemBuilder itemNm(String itemNm) {
            this.itemNm = itemNm;
            return this;
        }

        public Item.ItemBuilder ctgId(String ctgId) {
            this.ctgId = ctgId;
            return this;
        }

        public Item.ItemBuilder price(BigDecimal price) {
            this.price = price;
            return this;
        }

        public Item.ItemBuilder sellTypeCd(String sellTypeCd) {
            this.sellTypeCd = sellTypeCd;
            return this;
        }

        public Item build() {
            return new Item(this.itemCd, this.itemNm, this.ctgId, this.price, this.sellTypeCd);
        }

        public String toString() {
            return "Item.ItemBuilder(itemCd=" + this.itemCd + ", itemNm=" + this.itemNm + ", ctgId=" + this.ctgId + ", price=" + this.price + ", sellTypeCd=" + this.sellTypeCd + ")";
        }
    }
}

상속 받은 클래스 Builder 구현

@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;
    }
}

상속받은 클래스에서는 다음과 같이 생성자에 Builder 어노테이션을 추가해 Builder를 구현할 수 있다.

public class Child extends Parent {
    private final String childName;
    private final int childAge;

    public Child(String parentName, int parentAge, String childName, int childAge) {
        super(parentName, parentAge);
        this.childName = childName;
        this.childAge = childAge;
    }

    public static Child.ChildBuilder builder() {
        return new Child.ChildBuilder();
    }

    public String getChildName() {
        return this.childName;
    }

    public int getChildAge() {
        return this.childAge;
    }

    public static class ChildBuilder {
        private String parentName;
        private int parentAge;
        private String childName;
        private int childAge;

        ChildBuilder() {
        }

        public Child.ChildBuilder parentName(String parentName) {
            this.parentName = parentName;
            return this;
        }

        public Child.ChildBuilder parentAge(int parentAge) {
            this.parentAge = parentAge;
            return this;
        }

        public Child.ChildBuilder childName(String childName) {
            this.childName = childName;
            return this;
        }

        public Child.ChildBuilder childAge(int childAge) {
            this.childAge = childAge;
            return this;
        }

        public Child build() {
            return new Child(this.parentName, this.parentAge, this.childName, this.childAge);
        }

        public String toString() {
            return "Child.ChildBuilder(parentName=" + this.parentName + ", parentAge=" + this.parentAge + ", childName=" + this.childName + ", childAge=" + this.childAge + ")";
        }
    }
}

다음과 같이 부모 클래스의 필드까지 모두 Builder 내부에 구현된 것을 확인할 수 있다.

Child child = Child.builder()
  .parentName("Andrea")
  .parentAge(38)
  .childName("Emma")
  .childAge(6)
  .build();

@SuperBuilder

Lombok 1.18.2 이후부터는 @SuperBuilder 어노테이션으로 상속되는 클래스의 Builder를 생성할 수 있다.

@SuperBuilder
public class Parent {
    private final String parentName;
    private final int parentAge;
}

@SuperBuilder
public class Child extends Parent {
    private final String childName;
    private final int childAge;
}
public class Parent {
    private final String parentName;
    private final int parentAge;

    protected Parent(Parent.ParentBuilder<?, ?> b) {
        this.parentName = b.parentName;
        this.parentAge = b.parentAge;
    }

    public static Parent.ParentBuilder<?, ?> builder() {
        return new Parent.ParentBuilderImpl();
    }

    private static final class ParentBuilderImpl extends Parent.ParentBuilder<Parent, Parent.ParentBuilderImpl> {
        private ParentBuilderImpl() {
        }

        protected Parent.ParentBuilderImpl self() {
            return this;
        }

        public Parent build() {
            return new Parent(this);
        }
    }

    public abstract static class ParentBuilder<C extends Parent, B extends Parent.ParentBuilder<C, B>> {
        private String parentName;
        private int parentAge;

        public ParentBuilder() {
        }

        protected abstract B self();

        public abstract C build();

        public B parentName(String parentName) {
            this.parentName = parentName;
            return this.self();
        }

        public B parentAge(int parentAge) {
            this.parentAge = parentAge;
            return this.self();
        }

        public String toString() {
            return "Parent.ParentBuilder(parentName=" + this.parentName + ", parentAge=" + this.parentAge + ")";
        }
    }
}
public class Child extends Parent {
    private final String childName;
    private final int childAge;

    protected Child(Child.ChildBuilder<?, ?> b) {
        super(b);
        this.childName = b.childName;
        this.childAge = b.childAge;
    }

    public static Child.ChildBuilder<?, ?> builder() {
        return new Child.ChildBuilderImpl();
    }

    private static final class ChildBuilderImpl extends Child.ChildBuilder<Child, Child.ChildBuilderImpl> {
        private ChildBuilderImpl() {
        }

        protected Child.ChildBuilderImpl self() {
            return this;
        }

        public Child build() {
            return new Child(this);
        }
    }

    public abstract static class ChildBuilder<C extends Child, B extends Child.ChildBuilder<C, B>> extends ParentBuilder<C, B> {
        private String childName;
        private int childAge;

        public ChildBuilder() {
        }

        protected abstract B self();

        public abstract C build();

        public B childName(String childName) {
            this.childName = childName;
            return this.self();
        }

        public B childAge(int childAge) {
            this.childAge = childAge;
            return this.self();
        }

        public String toString() {
            String var10000 = super.toString();
            return "Child.ChildBuilder(super=" + var10000 + ", childName=" + this.childName + ", childAge=" + this.childAge + ")";
        }
    }
}

ChildBuilder는 ParentBuilder를 상속받고 있는것을 볼 수 있다.

참고

Last updated