JVM/Java

[Lombok] 자주 사용되는 Lombok Annotation 기능정리

헹창 2022. 9. 4.
반응형

먼저 자바 라이브러리 Lombok 다운로드 및 적용방법은 다음 게시글을 참고하자.

 

Java 필수 라이브러리 Lombok 사용, 적용방법

Java 필수 라이브러리 Lombok 사용, 적용방법 Lombok 라이브러리는 VO(Value Object) 나 DTO(Data Transfer Object) 생성 시 혁신적으로 코드를 줄여주는 라이브러리 입니다. 얼마나 혁신적으로 코드를 줄이는지..

aljjabaegi.tistory.com

 

@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

https://dingue.tistory.com/14

 

728x90
반응형

댓글

추천 글