결론
컴파일단계에서는 구현코드가 줄어드나, 실행코드가 많은 것은 여전함
코드 자동 생성으로 인해 개발자의 의도와는 다르게 구현될 수 있음(ex 순환참조)
Lombok 이 개발자를 도와주는 도구임에는 변함없음
모든 기술이 역사적으로 그래왔듯이, 사용자 하기 나름java record를 사용하되, 필요한 기능만 구현해도 무방함
(필자는 delombok 후 record + builder 라이브러리로 재구성했음)Lombok 사용자는 지금도 늘고 있으며 커뮤니티도 활발함
향 후 패치에서 점진적으로 개선될 것으로 보임
시작
Student 객체로 데이터를 설계해보자
public class Student {
private String name;
private Integer age;
private Grade grade;
private enum Grade {
A, B, C, D, F
}
}
그리고 아래의 추가 요청사항이 있다고 하자
get/set 설정
동일한 데이터에 대해 처리하는 로직 추가
객체 로그 출력 이쁘게
빌더 패턴 적용
.....
import java.util.Objects;
public class Student {
private String name;
private Integer age;
private Grade grade;
private Student(Builder builder) {
setName(builder.name);
setAge(builder.age);
setGrade(builder.grade);
}
public static Builder builder() {
return new Builder();
}
private enum Grade {
A, B, C, D, F
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age) && grade == student.grade;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", grade=" + grade +
'}';
}
public static final class Builder {
private String name;
private Integer age;
private Grade grade;
private Builder() {
}
public Builder name(String val) {
name = val;
return this;
}
public Builder age(Integer val) {
age = val;
return this;
}
public Builder grade(Grade val) {
grade = val;
return this;
}
public Student build() {
return new Student(this);
}
}
}
WOW... 😱 BoilerPlate 코드가 많아졌다...
하지만 lombok 을 사용하면
import lombok.*;
@Data
@Builder
public class Student {
private String name;
private Integer age;
private Grade grade;
private enum Grade {
A, B, C, D, F
}
}
코드가 순식간에 줄어들었다.
불변 객체로써 쓰고 싶다면 클래스 위에 @Value(staticConstructor = "of")
붙이면 된다.
아래와 같이 lombok이 잘 적용된 것을 알 수 있다.
public class ImmutableObjTest {
@Test
void lombokTest() {
Student fred1 = Student.of("Fred", 21, Student.Grade.A);
Student fred2 = Student.of("Fred", 21, Student.Grade.A);
Student martie = Student.of("Martie", 20, null);
assertEquals(fred1, fred2);
assertNotEquals(fred1, martie);
System.out.println("I am a "+fred1);
System.out.println("I am a "+fred2);
System.out.println("I am a "+martie);
}
}
I am a Student(name=Fred, age=21, grade=A)
I am a Student(name=Fred, age=21, grade=A)
I am a Student(name=Martie, age=20, grade=null)
자주 사용되는 기능
@Data
아래의 애너테이션이 모두 포함되어 있음
@Getter
@Setter
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
@Value
불변 객체를 만들 때 사용
아래의 애너테이션이 모두 포함되어 있음
@Getter
@Getter
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@AllArgsConstructor
@ToString
@EqualsAndHashCode
주의점
편한 만큼 과도한 애너테이션의 남용으로 실제 실행 코드 증가
관례 기반 코드 스타일로 컴파일 단계에서 잠재적 오류를 파악 하기 힘듦
잠재된 오류 예제
아래에 어떤 개발자가 name 만큼은 꼭 받고 싶어서 final 선언했다고 가정하자
@Data
@Builder
public class Student {
private final String name;
private Integer age;
private Grade grade;
public enum Grade {
A, B, C, D, E
}
}
public class FinalFieldButGeneratedTest {
@Test
void lombokTest() {
Student fred1 = Student.builder()
.name("fred")
.age(20)
.grade(Student.Grade.A)
.build();
Student fred2 = Student.builder()
.name("fred")
.age(20)
.grade(Student.Grade.A)
.build();
Student martie = Student.builder()
.age(30)
.grade(Student.Grade.B)
.build();
assertEquals(fred1, fred2);
assertNotEquals(fred1, martie);
System.out.println("I am a " + fred1);
System.out.println("I am a " + fred2);
System.out.println("I am a " + martie);
}
}
실행하면 아래와 같이 null 값이 들어가게 된다.
I am a Student(name=fred, age=20, grade=A)
I am a Student(name=fred, age=20, grade=A)
I am a Student(name=null, age=30, grade=B)
@ToString
순환 참조 문제도 있다. 코드가 길어질 것 같으니 pass..
대체재. Java record type
자바 14에 첫 등장하여 16에 안정화가 된 record 타입도 있다.
public record Student(
String name,
Integer age,
Grade grade
) {
public static Student of(String name, Integer age, Grade grade) {
return new Student(name, age, grade);
}
public enum Grade {
A, B, C, D, F
}
}
아까 전의 테스트코드와 완벽 호환된다. 값 호출도 get이 아닌 칼럼 명 그대로 호출한다.
public class ImmutableObjTest {
@Test
void lombokTest() {
Student fred1 = Student.of("Fred", 21, Student.Grade.A);
Student fred2 = Student.of("Fred", 21, Student.Grade.A);
Student martie = Student.of("Martie", 20, null);
assertEquals(fred1, fred2);
assertNotEquals(fred1, martie);
System.out.println("I am a " + fred1);
System.out.println("I am " + fred1.age() + " years old");
System.out.println("I got " + fred1.grade());
}
}
I am a Student[name=Fred, age=21, grade=A]
I am 21 years old
I got A