Daily Notebook

[Java] Lombok 소화하기

by 배부른코딩로그
💡 귀찮은 반복 작업을 완벽하게 해소해주는 롬복(Lombok). 한 번 제대로 정리해보자!

목표

  • Lombok 라이브러리가 무엇인지 왜 사용하는지 설명할 수 있다.
  • Lombok 관련 애노테이션 종류와 각각의 쓰임을 설명할 수 있다.
  • Lombok 관련 예제를 정리하는 공간으로 활용할 수 있다.

 

롬복은 자바 개발에 널리 사용되는 매우 편리한 라이브러리이다

생성자나 Getter, Setter 등 보일러 플레이트성 코드를 매번 수동으로 작성할 필요가 없기 때문일 것이다.

작성을 안했는데 언제 생성이 되는 것일까? 이를 컴파일 타임(Compile-time)에서 자동으로 생성해준다.

게다가, IDE 내에서도 모든 메서드에 적상적으로 접근할 수 있다.

 

롬복에서 일반적으로 사용하는 애노테이션은 다음과 같다.

  • @Data
  • @Builder and @SuperBuilder
  • @NonNull
  • @Cleanup
  • @RequiredArgsConstructor(onConstructor = @__(@Inject))
  • var, val

 

Lombok

@Data

JPA에서 DTO를 만들 때, 주로 사용하는 애노테이션이다.

이에 대한 예제는 다음과 같다.

@Data
public final class Person {
    private String firstName;
    private String LastName;
    private int age;
    private Company company;
}

@Data 애노테이션에는 아래와 같은 애노테이션 집합이 하나에 묶여 있음을 알아야 한다.

  • @Getter
  • @Setter
  • @ToString
  • @EqualsAndHashCode
  • @RequiredArgsConstructor

Getter/Setter와 toString(), hashCode(), equals() 등 일반적인 메서드를 기본적으로 생성한다.

따라서, 사용자 지정으로 toString() 또는 equals()에 구현할 수 있는 유연성을 갖추고 싶다면

클래스 정의에 직접 추가하면 Lombok 기본 보일러 플레이트 내용을 덮어쓸 수 있다.

 

@Builder and @SuperBuilder

@Builder

Builder 패턴의 보일러 플레이트이며, 클래스에 필드가 여러 개 있어 초기화가 복잡할 때 유용하다.
사용 예제는 다음과 같다.

@Builder
public final class Person {
    private String firstName;
    private String LastName;
    private int age;
    private Company company;
}

Builder 패턴을 사용한 객체 생성 예제는 다음과 같다.

final Person codify = Person.builder
		.firstName('dify')
		.lastName('Co')
		.age(30)
		.build();

Builder 패턴의 장법은 각 파라미터에 따른 생성자 코드를 작성하지 않아도 됨에 있다.

객체 생성 초기화 시, lastName이 필요가 없다면 없는대로 생성이 가능하다.

 

그런데, @Builder는 상속에서 어떻게 작동될까?

@SuperBuilder를 사용하여 상속에 따른 Builder 패턴을 적용할 수 있다.

 

@SuperBuilder

@SuperBuilder
public final class Person {
	private String firstName;
	private String LastName;
	private int age;
	private Company company;
}

@SuperBuilder
public class Staff {
	private String position;
	private int yearOfExperience;
	private List<String> abilities;
}
final Person codify = Staff.builder()
		.firstName('dify')
		.lastName('Co')
		.age(30)
        .position("developer")
        .yearOfExperience(3)
        .abilities(Collections.singletonList("Spring", "React"))
        .build();

위와 같이 @Data와 @Builder의 함께 사용하면, 컴파일 중 오류가 발생한다.

그 이유는 @Data와 @Builder 만 사용했기 때문에 기본 생성자가 누락되었기 때문이다.

이 경우, Java의 직렬화/역직렬화 라이브러리가 실행됨에 기본 생성자가 누락되어 역직렬화 단계에서 오류가 난다.

 

이를 위해 @NoArgsConstructor 혹은 @AllArgsConstructor를 추가한다.

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Company {
    private String name;

    public String changeA2B() {
        Company company = Company.builder().name("CODIFY").builder();
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString();
        Company company2 = mapper.readValue(json, Company.class);
        System.out.println(company2); // Company [name=CODIFY]
    }
}

@NoArgsConstructor 및 @AllArgsConstructor 애노테이션 이름에서 의미가 드러난다 : )

 

// @NoArgsConstructor
public Company() {}

// @AllArgsConstructor, 필드가 n개면, n개의 args
public Company(String name) {
    this.name = name;
}

 

@NonNull

롬복은 @NonNull 애노테이션을 통해 null-check도 손쉽게 도와준다.

if (param == null) throw new NullPointerException("Cannot invoke ~ because 'param' is null");
컴파일시, 메서드 스코프 맨 위에 삽입된다.
public void printPerson(@NonNull Person person) {
    System.out.println(person.toString()); // Here, no need to worry about NPE!
}

public String upperCase(@NonNull String str) {
    return str.toUpperCase(); // Here, no need to worry about NPE!
}

단, @NonNull 애노테이션은 롬복이 생성한 생성자나 메서드에서만 작동한다는 점이다

다음과 같은 경우 @NonNull이 무시된다.

@Getter
@Setter
public class LombokTest {
    @NonNull
    private Integer id;

    @NonNull
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@Getter와 @Setter를 사용하지 않기 위해 Override 처리한 getName과 setName의 경우가 이에 해당된다.

public class LombokTest {
    @NonNull
    private Integer id;

    @NonNull
    private String name;

    @NonNull
    public Integer getId() {
        return this.id;
    }

    public void setId(@NonNull Integer id) {
        if (id == null) {
            throw new NullPointerException("id is null");
        }
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

컴파일 시 위와 같이 작성됨을 주의해야 한다.

 

@ Cleanup

@Cleanup 애노테이션은 스코프 내에서 리소스를 참조 완료 후 리소스를 릴리즈함을 간편하게 할 수 있다.

File I/O를 통해 간단한 예제를 가져와봤다.

public byte[] read(String infile, String outfile) {
    @Cleanup InputStream in = new FileInputStream(infile);
    @Cleanup OutputStream out = new FileOutputStream(outfile);

    byte[] b = new byte[10000];
    while (true) {
        int r = in.read(b);
        if (r == -1) break;
        out.write(b, 0, r);
    }
    
    return b;
}

롬복은 생성된 InputStream 및 OutputStream은 참조 후 자동으로 닫아준다.

// if else check, then
in.close();
// if else check, then
out.close();

try~catch~finally를 통해 닫을 때 보일러 플레이트성 코드를 매번 기입했는데,

이를 애노테이션 한 방에 처리를 할 수 있다는 점에서 아주 유용하다.

 

@RequiredArgsConstructor

여기서는 Singleton Pattern과의 "통합"을 위한 다른 용도를 추가적으로 정리한다.

  • @RequiredArgsConstructor(onConstructor = @__(@Inject)): 해당 생성자에 @Inject 을 생성.
  • @RequiredArgsConstructor(onConstructor = @_(@Autowired)): 해당 생성자에 @Autowired 를 생성.

 

 

출처

 

단어

  • *보일러플레이트 코드(Boilerplate code)
    컴퓨터 프로그래밍에서 최소한의 변경으로 여러 곳에서 재사용되며, 반복적으로 비슷하게 작성되는 코드를 의미한다.

 

 

Last Updated. 2022. 06. 07.

 

반응형

'Java' 카테고리의 다른 글

[Java] 특정 디렉토리 및 파일 모니터링  (0) 2022.06.10
[Java] 파일 입출력 소화하기  (0) 2022.06.09
[Java] Null Safe한 자바 컬렉션 정렬  (0) 2022.05.24
[Java] Enum 소화하기  (0) 2022.05.09
[Java] YAML 활용하기  (0) 2022.05.04

블로그의 정보

배부른코딩로그

배부른코딩로그

활동하기