Keep calm and code on
Lambda를 이용하여 null 안전하게 처리하기 본문
NullPointerException은 자바 언어로 개발하면서 가장 친근하게 느껴지는 예외이다. 컴파일 레벨에서 쉽사리 잡히지도 않거니와 경우의 수가 너무나 다양하기 때문에 이를 잘 처리하는 것도 쉽지 않다. 코틀린처럼(Null Safety - Kotlin Programming Language) 예쁘게 null을 처리할 수 있는 방법이 있지도 않으며 Optional을 모든 경우에 쓰기에는 복잡성만 한가득 늘어나게 된다.
일반적으로 애플리케이션간 혹은 메시지를 주고 받을때 주로 DTO형식을 많이 이용하게 된다. 외부지점에서 DTO를 받는 경우 보통 이를 정제/변환하는 과정을 거쳐 활용을 하도록 로직을 작성하는 경우가 많은데, 이러한 정제/변환 과정에서 다음과 같은 코드를 필연적으로 많이 만날수밖에 없다.
String name = book.getAuthor().getName();
book 객체 하위 author의 값이 제대로 할당되지 않은 경우, 즉 null인 경우를 자주 만나곤 한다. 만약 NullPointerException을 예방하기 위해선 다음과 같이 작성하여야 한다.
String name = book.getAuthor() != null? book.getAuthor().getName() : null;
book이 null인 상황도 존재할것이다. 이러한 경우에는 위의 코드에 앞서서 book이 null인지 여부도 확인을 해야한다.
String name = null;
if(book != null) {
name = book.getAuthor() != null? book.getAuthor().getName():null;
}
단순하게 값을 하나 가져오는데 있어서 준비과정이 지나치게 길어지는 느낌이 든다. 이러한 경우 Lambda를 이용하여 복잡한 null 검증 로직을 쉽게 처리할 수 있을 것이다.
public static <R> R nullsafe(Supplier<R> supplier) {
try {
return supplier.get();
} catch (NullPointerException e) {
return null;
}
}
NullPointerException을 메서드 바디에서 나도록 만들어버리고 외부에 전파되지 않도록 기본값을 설정하여 주었다. Supplier를 이용하여, null인지 여부를 파라미터 레벨에서는 검증하지 못하도록 설정하였다. 작성된 메서드는 아래와 같이 사용할 수 있다.
String name = nullsafe(() -> book.getAuthor().getName());
이렇게 할 경우 book이 null인지, name이 null인지 여부를 일일히 체크하지 않고 간결하게 값을 가져올 수 있다.
이때 유의하여야 할 점은 method reference를 이용해서는 안된다는 점이다. 할당된 값이 없기에 메서드 레퍼런스 구문이 제대로 동작하지 않게 된다.
Book book = null;
// name = null;
String name = nullsafe(() -> book.getAuthor());
// throw nullPointerException
Strign npeName = nullsafe(Book::getAuthor);
값이 존재하지 않는 경우 null 대신 기본값을 넣는 경우도 생각해볼수 있을것이다.
public static <R> R nullsafe(Supplier<R> supplier, R defaultValue) {
try {
return supplier.get();
} catch (NullPointerException e) {
return defaultValue;
}
}
public static <R> R nullsafe(Supplier<R> mapper) {
return nullsafe(mapper, null);
}
위의 코드는 아래와 같이 사용할 수 있을것이다.
Book book = new Book(1L, "Harry Porter", /*author*/ null);
String authorName = nullsafe(()->book.getAuthor().getName(), "J. K. Rowling");