Lombok에서 제공하는 이 어노테이션은 생성자 인자를 메서드 체인을 통해 명시적으로 대입하여 생성자를 호출할 수 있게 빌더 클래스를 생성 해준다. 빌더 클래스와 IDE의 자동 완성 기능을 같이 활용하면 생성자 작성 시 오기입 확률과 인자를 누락할 확률을 획기적으로 낮출 수 있습니다.
doc을 보면 @Builder
는 생성자, 메서드 또는 클래스 레벨에서 쓰일 수 있다고 설명되어 있다. 또한 클래스 레벨에서 쓰일 경우 기본적으로 전체 멤버를 생성자의 매개값으로 갖는 private
생성자를 만들어 준다. 이 생성자는 @XArgsConstructor
(NoArgs, RequiredArgs) 또는 어떤 생성자도 클래스 내부에 선언하지 않았을 경우에만 생성된다. 반대로 위의 두 조건 중 하나를 했을 경우, 모든 필드를 매개값으로 하는 생성자를 자동으로 선언해서 사용한다. 따라서 이 경우 All Args Constructor가 없으면 컴파일 에러가 발생합니다.
정리하면 @Builder
클래스 레벨에서 쓰려면 All args constructor가 있어야 한다. 이 외의 생성자는 컴파일 에러를 일으킵니다.
예를 들어 @NoArgsConstructor
를 쓰고 클래스 레벨의 @Builder
를 쓰게 되면 All args constructor 없이 기본 생성자만 선언한 것과 같으므로 컴파일 에러가 뜹니다.
그런데 JPA나 json parser와 같은 라이브러리를 쓸 때에는 반드시 클래스에 기본 생성자가 있어야 한다. 이 경우 @NoArgsConstructor
를 쓸 수 밖에 없다. 그러면 클래스 레벨에서 @Builder
를 쓸 수가 없어집니다. 방법은 전체 필드를 사용하는 생성자를 직접 선언하고 그 생성자에 @Builder
어노테이션을 쓰든가, 아니면 @NoArgsConstructor
와 @AllArgsConstructor
를 모두 쓰면 됩니다.
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BuildEx {
private String name;
private String phoneNum;
private List<Example> example = new ArrayList<Example>();
}
public class BuildExApp{
public static void main(String[] args){
BuildEx buildEx =BuildEx.builder()
.name("재열")
.phoneNum("010-1234-5678")
.build();
System.out.println(buildEx.toString());
}
}
BuildEx(name=재열, phoneNum=010-1234-5678, example=null)
@Builder.Default
를 쓰면 됩니다.@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BuildEx {
@Builder.Default
private String name="재열";
private String phoneNum;
private List<Example> example = new ArrayList<Example>();
}
public class BuildExApp{
public static void main(String[] args){
BuildEx buildEx =BuildEx.builder()
.phoneNum("010-1234-5678")
.build();
System.out.println(buildEx.toString());
}
}
BuildEx(name=재열,phoneNum=010-1234-5678, example=null)
public class BuildExApp{
public static void main(String[] args){
BuildEx buildEx = BuildEx.builder().phoneNum("010-1234-5678").build();
System.out.println(buildEx.toString());
BuildEx buildEx1 = new BuildEx();
System.out.println(buildEx1.toString());
BuildEx(name=재열, phoneNum=010-4623-2891,example=null)
BuildEx(name=재열, phoneNum=null,example=[])
빌더를 통해 만든 객체는 List
필드가 null
로 초기화 되었고, 빌더 없이 기본 생성자로 생성한 pojo1의 List
필드는 정상적으로 empty List로 초기화되었다. 클래스에서는 분명히 new ArrayList<>
로 초기화를 했는데 왜 이런걸까?
doc을 살펴보면 빌더는 아래와 같이 코드를 만들어준다.
class Example<T> {
private T foo;
private final String bar;
private Example(T foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public static <T> ExampleBuilder<T> builder() {
return new ExampleBuilder<T>();
}
public static class ExampleBuilder<T> {
private T foo;
private String bar;
private ExampleBuilder() {}
public ExampleBuilder foo(T foo) {
this.foo = foo;
return this;
}
public ExampleBuilder bar(String bar) {
this.bar = bar;
return this;
}
@java.lang.Override public String toString() {
return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
}
public Example build() {
return new Example(foo, bar);
}
}
}
@Builder.Default
를 쓰면 됩니다.@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BuildEx {
@Builder.Default
private String name="재열";
private String phoneNum;
@Builder.Default
private List<Example> example = new ArrayList<Example>();
}
public class BuildExApp{
public static void main(String[] args){
BuildEx buildEx = BuildEx.builder().phoneNum("010-1234-5678").build();
System.out.println(buildEx.toString());
BuildEx buildEx1 = new BuildEx();
System.out.println(buildEx1.toString());
```java
BuildEx(name=재열, phoneNum=010-4623-2891,example=[])
BuildEx(name=재열, phoneNum=null,example=[])