[Lombok] 자주 사용되는 Lombok Annotation 기능정리
@Getter, @Setter
필드에 @Getter/@Setter 을 붙이면, 해당 필드에 대한 기본 getter/setter 메소드를 생성해준다.
접근 제한자 AccessLevel
@Getter/@Setter 에서의 접근 제한자 설정을 할 수 있다.
만약 생성되는 getter/setter 에 AccessLevel을 명시해주지 않으면 접근 제한자는 public이 된다.
- PUBLIC
- PROTECTED
- PRIVATE
- PACKAGE
- NONE
- MODULE
@Getter/@Setter 는 필드가 아닌 클래스에도 사용할 수 있는데, 클래스에 사용할 경우 static이 아닌 전체필드에 getter/setter가 적용된다. 만약 특정 필드에서 @Getter/@Setter의 생성을 막고 싶다면 AccessLevel.NONE을 사용하면 해당 필드는 getter/setter 메소드를 생성하지 않는다.
예제 코드
public Class Sample {
@Getter
private String attr1;
@Getter(AccessLevel.PRIVATE)
private String attr2;
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private String attr2;
public String getAttr1() {
return this.attr1;
}
private String getAttr2() {
return this.attr2;
}
}
@Getter
public Class Sample {
private String attr1;
@Getter(AccessLevel.NONE)
private String attr2;
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private String attr2;
public String getAttr1() {
return this.attr1;
}
}
Constructor Annotation
생성자를 자동으로 생성해주는 애노테이션
@NoArgsConstructor
파라미터가 없는 생성자를 생성한다
이를 @NoArgsConstructor(AccessLevel.PROTECTED) 이렇게 사용할 경우 모든 필드에 대한 값이 들어가야함을 보장하고 싶을 때, 기본 생성자 호출을 막음으로써 이후에 발생할 수 있는 문제를 사전 차단할 수 있다.
예제코드
@NoArgsConstructor
public Class Sample {
private String attr1;
public Sample(String attr1) {
this.attr1 = attr1;
}
}
// 실제 자바 코드
public Class Sample {
private String attr1;
public Sample(String attr1) {
this.attr1 = attr1;
}
public Sample() {
}
}
주의점
- 필드들이 final로 생성되어 있는 경우에는 필드를 초기화할 수 없기 때문에 생성자를 만들 수 없어 에러가 발생된다.
이 때, @NoArgsConstructor(force=true) 옵션을 이용해 final 필드를 0, false, null 등으로 강제 초기화시켜 생성자를 만들 수 있다. - @NonNull 같이 필드에 제약조건이 설정되어 있는 경우, 생성자 내 null-check 로직이 생성되지 않는다.
후에 초기화를 진행하기 전까지 null-check 로직이 발생하지 않는 점을 염두해야 한다.
@RequiredArgsConstructor
초기화되지 않은 모든 final 필드, @NonNull로 마크되어 있는 필드들에 대한 생성자를 자동으로 생성한다.
예제코드
@RequiredArgsConstructor
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
public Sample(String attr2, @NonNull String attr3) {
if(attr3 == null) {
throw new NullPointerException("attr3 is marked non-null but is null");
} else {
this.attr2 = attr2;
this.attr3 = attr3;
}
}
}
주의점
- 파라미터의 순서는 클래스에 있는 필드 순서에 맞춰 생성자가 생성한다.
- @NonNull 필드들은 null-check 가 추가적으로 생성되며, @NonNull이 마크되어 있어도 파라미터에서 null 값이 들어온다면 생성자에서 NullPointerException이 발생한다.
@AllArgsConstructor
클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성한다.
@NonNull 마크되어 있는 필드는 생성자 내에서 null-check 로직을 자동적으로 생성한다.
예제 코드
@AllArgsConstructor
public Class Sample {
private String attr1;
private String attr2;
@NonNull
private String attr3
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private String attr2;
@NonNull
private String attr3
public Sample(String attr1, String attr2, @NonNull String attr3) {
if(attr3 == null) {
throw new NullPointerException("attr3 is marked non-null but is null");
} else {
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
}
}
}
staticName option
위의 세 생성자 애노테이션은 static factory를 만들 수 있는 옵션이 있다.
staticName 옵션을 사용해 생성자를 private으로 생성하고, private 생성자를 감싸는 static factory 메소드를 추가할 수 있다.
예제 코드
@RequiredArgsConstructor(staticName = "of")
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
public Sample(String attr2, @NonNull String attr3) {
if(attr3 == null) {
throw new NullPointerException("attr3 is marked non-null but is null");
} else {
this.attr2 = attr2;
this.attr3 = attr3;
}
}
public static Sample of(String attr2, @NonNull String attr3) {
return new Sample(attr2, attr3);
}
}
생성자 애노테이션 사용 시 주의점
- static 필드는 스킵된다.
- 파라미터의 순서는 클래스에 있는 필드 순서에 맞춰 생성하기 때문에 주의해야 한다.
만약, 클래스 필드의 순서를 바꾸었을 때 롬복이 자동적으로 생성자 파라미터 순서를 바꾸기 때문에 버그가 발생할 수 있다. - AccessLevel을 꼭 설정해주어야 한다 (기본 값 = public)
아래 애노테이션 모두 접근 제한자를 AccessLevel로 설정할 수 있다.
@EqualsAndHashCode
equals, hashCode를 자동생성해준다.
- equals : 두 객체의 내용이 같은지 동등성을 비교한다
- hashCode : 두 객체가 같은 객체인지 동일성을 비교한다
- callSuper 속성을 통해 메소드 자동 생성 시 부모 클래스의 필드까지 고려할지의 여부를 결정할 수 있다.
default = false (자신 클래스 값만 고려한다)
주의점
- 변경 가능한(Mutable) 객체에 아무런 파라미터 없이 그냥 사용할 경우 문제가 발생할 수 있다.
@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(sample); //true
sample.setSampleName("sample1");
samples.contains(sample); // false
위처럼 동일한 객체 (sample)임에도 set에 저장한 뒤에 해당 객체의 필드 값을 변경하면 hashCode가 변경되어 찾을 수 없게 된다. 그렇기 때문에
- 불변(Immutable) 클래스를 제외하고는 아무 파라미터 없는 @EqualsAndHashCode 사용은 지양하자.
- @EqualsAndHashCode(of={"필드"}) 형태로 동등성 비교에 필요한 필드를 명시하는 형태로 사용하도록 하자.
@Data
다음 애노테이션을 한 번에 설정해준다.
- @Getter / @Setter
- @RequiredArgsConstructor
- @ToString
- @EqualsAndHashCode
@NonNull
필드에 지정해주면 null-check 를 해주며, null 값이 들어왔을 경우 NPE 발생시킨다.
@ToString
toString 메소드를 대체하는 애노테이션으로 callSuper 속성을 통해 상속받은 클래스의 정보까지 출력여부를 결정할 수 있다.
@Value
@Data와 비슷하지만 불변으로 만들어준다고 생각하면 된다.
한 번 생성하면 변경할 수 없는 불변 객체를 만들기 위한 클래스 선언할 때는 @Data 대신 @Value를 사용하면 된다.
private 접근제어자와 final이 붙은 상수가 된다. 기본 생성자를 만들어주나 private이다.
@Wither
원하는 프로퍼티를 다시 할당할 때 해당 객체의 필드를 변경하는 것이 아닌 원하는 값으로 필드를 세팅한 새로운 객체를 반환해준다.
예제코드
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");
@Builder
다수의 필드를 가지는 복잡한 클래스의 경우, 생성자 대신 빌더를 사용하는 경우가 많다.
빌더 패턴을 직접 작성해보면 코딩량이 의외로 상당함을 깨닫게 된다.
이 때, @Builder를 사용하여 자동으로 해당 클래스에 빌더를 추가해줄 수 있다.
예제코드
@Builder
public class User {
private Long id;
private String username;
private String password;
@Singular
private List<Integer> scores;
}
User user = User.builder()
.id(1L)
.username("dale")
.password("1234")
.score(70)
.score(80)
.build();
// User(id=1, username=dale, password=1234, scores=[70, 80])
@Log , @Slf4j, @Log4j2
자동으로 로그 필드를 만들고, 해당 클래스의 이름으로 로커(Logger) 객체를 생성하여 할당해준다.
참고
https://www.projectlombok.org/features/all
https://projectlombok.org/api/lombok/AccessLevel
https://devk0ng.github.io/2021/07/30/lombok/#Value