자주 사용되는 lombok, 주의 사항

@Getter, @Setter

: 말그대로 getter, setter method를 생성해주는 놈이야!

AccessLevel을 명시해줌으로써 접근제한자를 지정 해 줄 수 있어.

1
2
3
4
5
6
7
8
9
public Class Sample {

@Getter
private String attr1;

@Getter(AccessLevel.PRIVATE)
private String attr2;

}

( setter의 경우 실무에서 무분별하게 사용할 경우 후에 의도치 않은 값의 변경으로 인한 어려움을 겪을 수 있기에 상황에 맞춰서 필요할 때만 사용할 것..)



@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor

: 모두 생성자 생성과 관련된 놈이야


@NoArgsConstructor

: 파라미터가 없는 생성자를 생성해줘.

이를 이용해 @NoargsConstructor(AccessLevel.PROTECTED) 이렇게 사용할 경우 만약 모든 필드에 대한 값이 들어가야함을 보장하고 싶을 때 기본 생성자 호출을 막음으로써 이후에 발생할 수 있는 문제를 사전에 차단할 수 있어.


@RequiredArgConstructor

: 초기화 되지 않은 모든 final 필드, @NonNULL로 마크되어있는 필드들에 대한 생성자를 자동으로 생성해줘. (파라미터의 순서는 클래스에 있는 필드 순서에 맞춰서 생성자가 생성)


@AllArgsConstructor

: 클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성해줘.


👏👏👏 주의 사항 👏👏👏

@RequiredArgsConstructor와 @AllArgsConstructor는 사용을 지양하는 것이 좋아!!

why?? 위의 두 어노테이션을 통해 생성된 생성자는 클래스의 필드 순서에 맞게 생성 된다고 하였지.

1
2
3
4
5
6
7
@AllArgsConstructor
public static class Sample {
private int attr1;
private int attr2;
}

Sample sample = new Sample(100, 200);

위의 이러한 코드가 있다고 가정 해 보자. 이후 생각을 해보니 attr1 과 attr2의 순서를 바꾸는 것이 가시성 측면에서 좋다고 생각이 들어 그 위치를 변경하였다.

1
2
3
4
5
@AllArgsConstructor
public static class Sample {
private int attr2;
private int attr1;
}

이런식으로 말이닷! 그럼 어떻게 될까?? 둘의 타입은 동일하기에 컴파일 시에

1
Sample sample = new Sample(100, 200);

이 코드에 있어 에러가 발생하지는 않을 것이다. 하지만 초기에 의도했던 attr1에 100, attr2에 200이 아닌 attr1에 200, attr2에 100이 들어가는 상황이 발생하게 되는 것이다. 이러한 상황이 발생할 수 있는 가능성을 제거하고자 사용을 지양한다.

그럼 어떻게 작성하는 것이 좋냐!?

@Builder를 사용하는 것을 권장해

1
2
3
4
5
6
7
8
9
10
11
12
13
public static class Sample {
private int attr1;
private int attr2;

@Builder
private Sample(int attr1, int attr2) {
this.attr1 = attr1;
this.attr2 = attr2;
}
}

//위처럼 할 경우 필드 순서를 이후 바꿨을 때 추가적인 부분을 생각할 필요가 x
Sample sample = Sample.builder().attr1(100).attr2(200).build();
  • @Builder를 클래스에 붙일 경우 모든 속성에 대해 지정가능
  • 생성자에 붙이는 경우는 모든 속성에 대해 원치 않고 특정 필드에 대해 원할 경우 특정 필드의 값을 채워주는 생성자에 어노테이션을 붙임으로써 사용가능
  • 생성자 메소드에도 사용가능
  • @Singular를 collection에 지정해줌으로써 빌더패턴에서 컬렉션에 하나씩 값을 추가하게 끔 할 수 있어.



@EqualsAndHashCode

: equals, hashCode를 자동생성 해줘

  • equals : 두 객체의 내용이 같은지 동등성을 비교
  • hashCode : 두 객체가 같은 객체인지 동일성을 비교
  • callSuper 속성을 통해 메소드 자동 생성 시 부모 클래스의 필드까지 고려할지의 여부를 결정할 수 있어(default : false 자신 클래스 값만 고려)

👏👏👏 주의 사항 👏👏👏

변경가능한(Mutable) 객체에 아무런 파라미터 없이 그냥 사용할 경우 문제가 발생할 수 있어. 주의해야할 부분은 아래와 같아.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@EqualAndHashCode
@AllArgsConstructor
@Setter
public static class Sample {
private Long sampleId;
private String sampleName;
}

Sample sample = new Sample(lL, "sample_one");

Set<Sample> samples = new HashSet<>();
samples.add(sample); //set에 객체 추가

samples.contains(order); //true

sample.setSampleName("sample1");
samples.contains(sample); // false

위처럼 동일한 객체임에도 set에 저장 후 필드 값을 변경하면 hashCode가 변경되면서 찾을 수가 없게 되어버려.

→ 그래서!!

  • 불변(Immutable) 클래스를 제외하고는 아무 파라미터 없는 @EqualsAndHashCode사용은 지양하자
  • @EqualsAndHashCode(of={“field”}) 형태로 동등성 비교에 필요한 필드를 명시하는 형태로 사용하자!



@Data

: @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode 를 한번에 설정해주는 애야!!

→ 위에서 지양한다는 어노테이션이 포함된 것을 보니 해당 어노테이션도 지양해야할 것이 보이쥐



@NonNull

: 필드에 지정해주면 Null을 check 해줘! 만약 null값이 들어오면 NPE 발생시켜.



@ToString

: toString() 메소드를 대체하는 어노테이션으로 callSuper 속성을 통해 상속받은 클래스의 정보까지 출력여부를 결정할 수 있다.



@Value

: @Data와 비슷하지만 불변으로 만들어준다고 생각하면 될 것 같애. private 접근제어자와 final이 붙은 상수가 되거등. 기본 생성자를 만들어주나 private이야.



@Wither

: 이 어노테이션도 불변과 관련된 녀석인데 원하는 프로퍼티를 다시 할당할 때 해당 Object의 필드를 변경하는 것이 아니라 원하는 값으로 필드를 셋팅한 새로운 Object를 뱉어내줘.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class witherSample {
private final Long id;
@Wither
private final String name;

public witherSample(Long id, String name) {
this.id = id;
this.name = name;
}
}

// .....
witherSample sample = new witherSample(1L, "sample");
wtiehrSample sample2 = sample.withName("sample2");








https://dingue.tistory.com/14

https://kwonnam.pe.kr/wiki/java/lombok/pitfall