java.lang.NullPointerException: Cannot invoke "com.keb.kebsmartfarm.entity.Plants.hasGrowingPlant()" because "this.plants" is null
일급 컬렉션 적용하면서 plants에서 현재 키우는 식물이 있는지 확인하는 hasPlant메서드를 실행하는데 문제가 생겼다.
아무래도 @Builder
나 @NoArgsConstructor
중에 문제가 있는 것 같아, 빈 생성자에 new Plants()
로 직접 삽입해줬다. 그런데도 문제가 있는 거 보니 @Builder
문제가 맞는 것 같다.
이 글을 참고해 생성된 Builder 코드를 직접 살펴보았다.
public static class ArduinoKitBuilder {
private Long kitNo;
private ReleasedKit releasedKit;
private String deviceName;
private String serialNum;
private LocalDateTime date;
private User user;
private Long userSeqNum;
private Plants plants;
private List<SensorData> sensorDataList;
ArduinoKitBuilder() {
}
public ArduinoKitBuilder kitNo(final Long kitNo) {
this.kitNo = kitNo;
return this;
}
public ArduinoKitBuilder releasedKit(final ReleasedKit releasedKit) {
this.releasedKit = releasedKit;
return this;
}
public ArduinoKitBuilder deviceName(final String deviceName) {
this.deviceName = deviceName;
return this;
}
public ArduinoKitBuilder serialNum(final String serialNum) {
this.serialNum = serialNum;
return this;
}
public ArduinoKitBuilder date(final LocalDateTime date) {
this.date = date;
return this;
}
@JsonIgnore
public ArduinoKitBuilder user(final User user) {
this.user = user;
return this;
}
public ArduinoKitBuilder userSeqNum(final Long userSeqNum) {
this.userSeqNum = userSeqNum;
return this;
}
@JsonIgnore
public ArduinoKitBuilder plants(final Plants plants) {
this.plants = plants;
return this;
}
public ArduinoKitBuilder sensorDataList(final List<SensorData> sensorDataList) {
this.sensorDataList = sensorDataList;
return this;
}
public ArduinoKit build() {
return new ArduinoKit(this.kitNo, this.releasedKit, this.deviceName, this.serialNum, this.date, this.user, this.userSeqNum, this.plants, this.sensorDataList);
}
public String toString() {
return "ArduinoKit.ArduinoKitBuilder(kitNo=" + this.kitNo + ", releasedKit=" + this.releasedKit + ", deviceName=" + this.deviceName + ", serialNum=" + this.serialNum + ", date=" + this.date + ", user=" + this.user + ", userSeqNum=" + this.userSeqNum + ", plants=" + this.plants + ", sensorDataList=" + this.sensorDataList + ")";
}
}
직접 살펴보니 @Builder
어노테이션은 클래스의 기본 생성자를 사용하지 않고 비어있는 빌더 클래스에서 매개변수로 받은 필드들을 채워나가는 형식으로 되어있는 것을 확인할 수 있었다. 그래서 기본 생성자로 채워넣든 필드를 초기화하든 관계 없이 매개변수로 넣어주지 않으면 채워지지 않는다.
아두이노 키트로 변환할 때 필요한 plants 값을 넣어주지 않았기 때문에 발생한 문제였다. 그래서 기본값을 넣어줘야 하는데 이 글을 참고해서 @Default
어노테이션으로 기본값을 넣어줬다. 그리고 직접 @Default
를 넣었을 때 생성되는 코드를 확인해봤다.
public class ArduinoKit {
...
private Plants plants;
...
private static Plants $default$plants() { return new Plants(); }
public static class ArudinoKitBuilder {
...
private boolean plants$set;
private Plants plant$value;
...
@JsonIgnore
public ArduinoKitBuilder plants(final Plants plants) {
this.plants$value = plants;
this.plants$set = true;
return this;
}
public ArduinoKit build() {
Plants plants$value = this.plants$value;
if (!this.plants$set) {
plants$value = ArduinoKit.$default$plants();
}
return new ArduinoKit(this.kitNo, this.releasedKit, this.deviceName, this.serialNum, this.date, this.user, this.userSeqNum, plants$value, this.sensorDataList);
}
}
}
직접 확인해보니 ArduinoKitBuilder 내부에 변수가 할당이 되었는지 확인하는 $##$set
이 boolean
으로 할당되어 있다. plants 메서드로 직접 plant$value
를 할당하면 설정이 되었다는 의미인 true
로 둔다.
이후 build 메서드에서 set이 false
일 경우에만 $default$plants
메서드로 새로 생성해서 할당한다.
결론적으로 해결은 했다. @Builder
역시 남들이 다 쓰니까 나 역시도 관습적으로 사용했는데 빌더가 어떻게 작용하는지와 어떤 추가 어노테이션이 있는지 몰라서 조금 헤멨었다. 그리고 빌더를 사용하면 당연히 기본 생성자를 사용해서 쓸 것이라고 생각했는데 아예 내부 정적 클래스에서 새로 값을 추가해가면서 변환하는 것이였다. 역시 뭐든 잘 알고 사용하지 않으면 탈이난다..
참고
'Today I Learned' 카테고리의 다른 글
Mermaid ERD 문법 (1) | 2024.12.02 |
---|---|
CSRF 토큰 확인 불가 문제 해결 (0) | 2024.06.29 |
필터에 @Component 등록 시 자동 등록 (0) | 2024.06.28 |