코드 참고를 하다보면 @AllArgsConstructor, @NoArgsConstructor를 사용하여 Entity 클래스를 정의한 것들을 자주 볼 수 있다.
나도 별 생각없이 사용하고 있었는데 어느 순간 NO <-> ALL 이 상충되는 의미 아닌가?
라는 의문이 생겨 이번 기회에 제대로 공부해 보려한다.
먼저 Lombok 공식 문서를 보면 아래 처럼 @NoArgsConstructor
/ @RequiredArgsConstructor
/ @AllArgsConstructor
이렇게 세개가 묶여있다.
이유는 결국 세 어노테이션의 역할이 생성자를 생성하는 기능을 하고, 앞에 붙은 Prefix가 생성자의 형태를 정하기 때문이다.
먼저 세개의 어노테이션의 기능을 간략하게 나타내면 아래와 같다.
@NoArgsConstructor
: 파라미터가 없는 디폴트 생성자를 생성@AllArgsConstructor
: 모든 필드 값을 파라미터로 받는 생성자를 생성@RequiredArgsConstructor
: final이나 @NonNull으로 선언된 필드만을 파라미터로 받는 생성자를 생성
Lombok 공식 문서의 내용을 보면 @NoArgsConstructor
어노테이션은 파라미터가 없는 디폴트 생성자를 자동으로 생성한다.
이 어노테이션을 사용하면, 클래스에 명시적으로 선언된 생성자가 없더라도 인스턴스를 생성할 수 있다. 즉 코드로 보면,
아래와 같이 Person 이라는 클래스를 생성했다고 가정하자
public class Person {
private String name;
private int age;
}
이 클래스에 @NoArgsConstructor
를 사용하면 아래와 같은 코드이고
@NoArgsConstructor
public class Person {
private String name;
private int age;
}
이 코드는 실직적으로 아래처럼 기본 생성자를 생성한 것과 동일하게 동작한다.
public class Person {
private String name;
private int age;
// 기본 생성자
public Person(){
}
}
📌 아래 코드와 같이 클래스에 final 처럼 초기화가 필요한 field에 @NoArgsConstructor을 사용한다면 compile error가 발생할 것이다!! 하지만,
@NoArgsConstructor(force = true)
처럼 force라는 옵션에 true 값을 주면, 모든 final fields는 0 / false / null로 초기화된다.
즉 아래 코드에서 name, age 에 대해서는 기본 생성자가 적용되지만 final로 선언한 Id에 대해서는 기본 생성자 적용이 아니라 Long 타입에 맞게 0 이 할당 될 것이다.
@NoArgsConstructor(force = true)
public class Person {
private String name;
private int age;
private final Long Id;
// 기본 생성자
public Person(){
}
}
Lombok 공식 문서의 내용을 보면 @AllArgsConstructor
어노테이션은 클래스의 모든 필드 값을 파라미터로 받는 생성자를 자동으로 생성한다고 한다. 이 어노테이션을 사용하면, 클래스의 모든 필드를 한 번에 초기화할 수 있다. 즉, 클래스 내부에 선언된 모든 field마다 하나의 parameter를 가진 생성자를 생성한다.
아래 코드처럼 Person 클래스에 @AllArgsConstructor 어노테이션을 적용하면
@AllArgsConstructor
public class Person {
private String name;
private int age;
}
아래와 같이 각 field에 맞는 생성자를 생성해 동작한다.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
Lombok 공식 문서의 내용을 보면 @RequiredArgsConstructor 어노테이션은 final이나 @NonNull으로 선언된 필드만을 파라미터로 받는 생성자를 자동으로 생성한다. 이 어노테이션을 사용하면, 클래스가 의존하는 필드를 간단하게 초기화할 수 있다. 즉, 특별한 처리가 필요한 각 field마다 하나의 parameter를 갖는 생성자를 생성해준다.
초기화되지 않은 모든 final fields와,선언될 때 초기화되지 않은 @NonNull로 표시된 field까지 parameter를 가진다.
특히 @NonNull이 달려있는 field의 경우, 생성되는 생성자 내부에 명시적인 null 체크 로직 또한 생성된다.
아래 코드처럼 final, Nullable=false 로 선언된 feild에 대해 아래와 같이 생성자를 생성해서 동작한다.
@RequiredArgsConstructor
public class Person {
private final String name;
private final int age;
@NonNull(message = )
privaate final Long Id
private String address;
// getters and setters
}
public class Person {
private final String name;
private final int age;
private String address;
public Person(final String name, final int age, Long Id) {
this.name = name;
this.age = age;
this.Id = Id
}
}
📌만약
@NonNull
이 붙어있는 field 중 어떠한 것이라도 null 값을 포함한다면 NullPointerException이 발생하게 된다.
생성자의 파라미터 순서는 클래스의 feild 순서로 정해지기 때문에 순서에 맞추지 않으면 의도와 다른 결과가 나타날 수 있다. 아래 코드처럼 @AllArgsConstructor 를 사용해 선언된 모든 field마다 하나의 parameter를 가진 생성자를 생성하면
@AllArgsConstructor
public class Person {
private String name;
private int age;
}
아래와 같이 동작하고
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
사용은 다음과 같이 하게된다. 여기서 만약 파라미터에 입력하는 값의 순서를 바꿔서 입력하면 아예 다른 결과가 발생하게 된다.
해당 예시의 경우 feild의 type이 다르기 때문에 괜찮지만 두 feild의 type이 같다면 입력 순서가 문제가 될 수 있다.
Person person = new Person("Alex",46);
이런 상황을 방지하는 방법으로는 아래처럼 @Build 어노테이션을 사용해 클래스를 구현하는 것이다.
public class Person {
private String name;
private int age;
@Builder
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
사용은 아래처럼 한다.
Person person = Person.builder().name("Alex").age(46).build();