[JAVA]@Builder(@Builder.default)
@Builder
-
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
를 모두 쓰면 됩니다.
@Bulder.Default
@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);
}
}
}
- 필드를 사용하는 생성자와 각 필드의 setter 메서드로 구성된 inner 클래스를 하나 만들어서 그 안에서 원본 클래스의 인스턴스를 리턴한다. 여기서 객체 타입의 필드가 있다고 가정하면, 당연히 내부 클래스에서는 이 객체 타입을 초기화하는 코드가 없다. 따라서 null로 초기화 될 것이고, 이 필드를 포함해서 원본 클래스가 만들어지므로 빌더를 통한 객체 생성에서는 객체 타입 필드가 null인 것이다.
- 이를 해결하기 위해서도 역시
@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=[])
Author And Source
이 문제에 관하여([JAVA]@Builder(@Builder.default)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@jyyoun1022/JAVABuilderBuilder.default
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
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);
}
}
}
- 필드를 사용하는 생성자와 각 필드의 setter 메서드로 구성된 inner 클래스를 하나 만들어서 그 안에서 원본 클래스의 인스턴스를 리턴한다. 여기서 객체 타입의 필드가 있다고 가정하면, 당연히 내부 클래스에서는 이 객체 타입을 초기화하는 코드가 없다. 따라서 null로 초기화 될 것이고, 이 필드를 포함해서 원본 클래스가 만들어지므로 빌더를 통한 객체 생성에서는 객체 타입 필드가 null인 것이다.
- 이를 해결하기 위해서도 역시
@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=[])
Author And Source
이 문제에 관하여([JAVA]@Builder(@Builder.default)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jyyoun1022/JAVABuilderBuilder.default저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)