> For the complete documentation index, see [llms.txt](https://dahye-jeong.gitbook.io/java/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dahye-jeong.gitbook.io/java/master-1/spring/2021-05-16-lombok.md).

# Lombok 활용하기

## Gradle 설정

* [공식페이지](https://projectlombok.org/setup/gradle)
* build.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

![image-20210515235710302](https://1681454910-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1i8u0kjXgtGdVt-887967055%2Fuploads%2Fgit-blob-cab6eeff82692eba991e2fefecaef498348b7daf%2Fimage-20210515235710302.png?alt=media)

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

## @Getter / @Setter

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

```java
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가 생성된 것을 확인할 수 있다.

![image-20210516001538291](https://1681454910-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1i8u0kjXgtGdVt-887967055%2Fuploads%2Fgit-blob-fee1cd2d2710c7042240bb69b5d7f2797af3622a%2Fimage-20210516001538291.png?alt=media)

## 생성자 자동 생성

### @NoArgsConstructor

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

### @AllArgsConstructor

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

```java
@NoArgsConstructor
@AllArgsConstructor
public class Member {

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

}
```

![image-20210516001852822](https://1681454910-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1i8u0kjXgtGdVt-887967055%2Fuploads%2Fgit-blob-80e2bb791c48617ab922e75656875f674c50edbc%2Fimage-20210516001852822.png?alt=media)

### @RequiredArgsConstructor

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

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

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

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

```java
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{

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

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

![image-20210516001012197](https://1681454910-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1i8u0kjXgtGdVt-887967055%2Fuploads%2Fgit-blob-dabcca3f5f2b85a5a24659c799e980119e57995a%2Fimage-20210516001012197.png?alt=media)

## @ToString

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

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

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

}
```

```java
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` 로 단순하게 빌더를 생성할 수 있다.

```java
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` 어노테이션 만으로 구현할 수 있다.

```java
@Builder
public class Item {
    private final String itemCd; // 필수
    private final String itemNm; // 필수
    private final String ctgId;  // 필수
    private final BigDecimal price; // 선택
    private final String sellTypeCd; // 선택
}
```

```java
//
// 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 구현

```java
@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를 구현할 수 있다.

```java
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 내부에 구현된 것을 확인할 수 있다.

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

#### @SuperBuilder

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

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

```java
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 + ")";
        }
    }
}
```

```java
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를 상속받고 있는것을 볼 수 있다.

## 참고

* <https://www.daleseo.com/lombok-popular-annotations/>
* <https://projectlombok.org/contributing/index>
* [김영한 스프링 핵심 원리 - 기본편](https://inf.run/deVM)
* <https://www.baeldung.com/lombok-builder-inheritance>
