Related POST
롬복 정리
1. Opening
이미 5년도 넘게 쓰고 있는 롬복을 세삼스레 정리하게 된 것은 이펙티브 자바와 JPA 공부덕이다
세삼스럽다.
2. 롬복이란
- 롬복을 이용하면 특정 코드를 줄여서 가독성과 편의성을 올릴 수 있다
- 특히 Getter와 Setter를 엄청나게 줄일 수 있어서 애용했었는데..
오히려 그럼으로써 getter/setter를 남용하게 되는 원인이기도 하다
3. @Getter/@Setter
- 게터메서드, 세터 메서드 생성
- 필드가 아닌 클래스에 선언하는 경우 해당 클래스의 모든 필드의 게터,세터 생성
- VO 클래스를 만들게 되는 경우 노가다에서 해방
4. 생성자
@NoArgsConstructor
: 파라미터 없는 기본 생성자 생성@AllArgsConstructor
: 모든 필드 값을 파라미터로 받는 생성자@RequiredArgsConstructor
final
,@NonNull
인 필드값만 파라미터로 받는 생성- 사실 서비스나 컨트롤러도 이걸로 생성하는게 가장 깔끔하다
- 어차피 컨트롤러는 서비스, 서비스는 JPA리포지토리등을 주입받음
- 중간에 빈을 바꿀 일은 없음 : 처음 빈이 로딩될때 전부 설정값 반영됨
- 그러면 해당 빈을 final로 하고 이 어노테이션을 사용하면 생성자 주입 방식 사용됨
@NonNull
: 메서드 파라미터에 사용, null이 파라미터로 전달되면 예외- access 필드로 접근 권한 수정 가능
@NoArgsConstructor(access = AccessLevel.PROTECTED)
- JPA 기본 스펙상 기본 생성자 필요
- protected로 제어하는 것까지 허용되며 new로 객체생성함을 막을 수 있음
5. @ToString
- 이클립스나 인텔리제이가 만들어주는 ToString을 자동으로 생성
- 롬복을 사용하기전에는 Apache Commons Lang 라이브러리의 ToStringBuilder를 사용
- 보통 문제가 되는게 JPA 양방향 관계에서 잘못 사용하면 무한 순환해서 스택이 터지는 오류
- 그래서 개인적으로 exclude로 특정 필드 제외하는 것을 애용 했음
- 공부를 해보니
of
를 권장 :exclude
보다 새로 객체를 추가해 줄 떄 마다 수정 비용이 적다고 한다
6. @EqualsAndHashCode
- equals(), hashcode()를 자동으로 생성해줌
- ToString,처럼 exclude, callSuper 사용 가능
- 객체 직렬화에서 제외할때 사용하는 transient 키워드 사용시 exclude 효과
- callSuper : 해당 속성을 통해 부모 클래스 필드까지 동일한지 체크 가능
- 성능 이슈가 발생할 수 있다고 한다(Set 자료 구조)
7. @Data
- @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode를 한번에
- 사실 조금만 공부하면 실무에서는 절대 사용하지 않아야 한다.
8. @Builder
- GoF 빌더 패턴을 간단하게 만들 수 있다
- 클래스에 설정시
@AllArgsConstructor
효과가 생기므로 기피 - 생성자 빌더 패턴 적용시 장점
- 필요한 데이터만 설정 가능
- 일반 생성자 사용시 필요없는 값에 더미값을 넣거나 해당 값이 없는 생성자를 새로 적용 필요
- 빌더 사용시 동적으로 처리 : 객체 생성 용이, 코드양 줄임
- 유연성 확보
- 파라미터 추가시 기존 코드 수정 필요. 생성 코드가 100개면 100개에 따른 수정이 필요
- 빌더 패턴 이용시 기존 코드에 영향을 주지 않음
- 가독성 확보
- 파라미터 증가시 리딩 효율 급격히 떨어짐
- 빌더 패턴 적용시 가독성 유지, 직관적으로 어떤 데이터 설정인지 파악 가능
- 불변성 확보
- Setter 사용은 불필요한 확장 가능성
- Open-Closed 위배
- 불필요한 코드리딩 필요
- 따라서 클래스 변수를 final로 선언하고 객체의 생성은 빌더에 맡겨야 한다
- Setter 사용은 불필요한 확장 가능성
- 필요한 데이터만 설정 가능
- advance
builderClassName
파라미터로 nested 빌더 클래스의 이름을 (클래스명Builder가 기본)builderMethodName
으로 빌더 클래스를 반환하는 static 메소드의 이름을 (builder()가 기본)buildMethodName
으로 객체를 반환하는 빌드 메소드의 이름 (build()가 기본)을 설정할 수 있다@Builder.Default
: 기본값 설정, 해당 값이 없으면 정해진 기본값을 토태로 빌더가 실행된다@Singular
: List등의 컬렉션에 사용- builder에서 값을 추가할 떄 사용되는 메서드 이름 입력
- 빈 컬렉션이 자동으로 생성되며, 이 컬렉션은 수정 불가
@Builder(toBuilder = true)
- 인스턴스에서도 builder 가능.
- 해당 인스턴스에서 일부 값만 변경한 새로운 객체 생성 가능
9. @Cleanup
try-with-resource
구문과 비슷한 효과- 차이점
try-with-resource
: 구문이 종료될 때AutoCloseable
인터페이스의 close()호출@Cleanup
: scope 종료시 어노테이션이 설정된 변수의 close() 호출
10. val
- 스칼라의 val 키워드 기능
- 객체 타입을 추론한 불변 값 선언
- 스클라와 달리 지역변수, foreach 구문에만 사용 가능
- 메소드 파라미터, 클래스 필드에 사용 불가
11. @Value
- @Data어노테이션과 비슷하지만 필드를 변경할 수 없는 불변 객체를 만듬
- 예시
1
2
3
4
5
6
public class TestValve {
String name;
int val1;
int val2;
} - 빌드되면 다음과 같아진다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65public final class TestValve {
private final String name;
private final int val1;
private final int val2;
public TestValve(final String name, final int val1, final int val2) {
this.name = name;
this.val1 = val1;
this.val2 = val2;
}
public String getName() {
return this.name;
}
public int getVal1() {
return this.val1;
}
public int getVal2() {
return this.val2;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof TestValve)) {
return false;
} else {
TestValve other = (TestValve)o;
if (this.getVal1() != other.getVal1()) {
return false;
} else if (this.getVal2() != other.getVal2()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
public int hashCode() {
int PRIME = true;
int result = 1;
int result = result * 59 + this.getVal1();
result = result * 59 + this.getVal2();
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
String var10000 = this.getName();
return "TestValve(name=" + var10000 + ", val1=" + this.getVal1() + ", val2=" + this.getVal2() + ")";
}
} @Value
는 val 언노테이션 사용 → val 어노테이션 사용 가능할 때만@Value
사용 가능
12 @Log
- 클래스 상단의 static final log 필드 자동 생성
- SLF4J 사용하는 경우 @Slf4j를 사용하면 된다.
13. @Synchronized
Synchronized
보다 더 자세한 설정이 가능한 어노테이션Synchronized
: static, insttance 단위로 락@Synchronized
: @파라미터로 입력받는 오브젝트 단위- 파라미터가 없는 경우 어노테이션이 사용된 메소드 단위 락
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class TestValve {
public static final Object OBJECT_LOCK = new Object();
String name;
String name2;
int value;
//파라미터로 입력받는 Object 단위 Lock
public int valCalc(int value2){
return value + value2;
}
public int getValue() {
return value;
}
public String getName() {
return name;
}
public String getName2() {
return name2;
}
}
롬복 사용시 부모 생성자 호출 불가
- 레퍼런스 : https://stackoverflow.com/questions/29740078/how-to-call-super-constructor-in-lombok
- 롬복에서 Parent에서
@AllArgsConstructor
- Parent를 extend한 Child에서
super
로 부모 생성자 호출 불가 - 리플랙션 사용시 슈퍼 클래스의 생성자를 호출할 수 없다
일반적인 경우
- 생성자에 builder 어노테이션을 추가하면 super 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Parent {
private final String parentName;
private final int parentAge;
}
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;
}
}
답변 갱신
@SuperBuilder
을 사용해서 상속되는 클래스 빌더 생성 가능- Lombok 1.18.2 이후부터 사용 가능