[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 를 생성.
출처
- Project Lombok, Reinier Zwitserloot & Roel Spilker, 2009-2022
- 빌더 패턴(Builder Pattern), 기계인간(John Grib), 2018.02.12~2021-10-16
- [Java] - Project Lombok(롬복), 주발2(in Tistory), 2021.06.20
단어
- *보일러플레이트 코드(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 |
블로그의 정보
배부른코딩로그
배부른코딩로그