NullPointerException은 초급자, 중급자, 남녀노소를 불문하고 모든 자바 개발자를 괴롭히는 예외이다.
토니 호어라는 영국 컴퓨터과학자가 처음 null 레퍼런스를 도입하였다. 그 당시에는 null 레퍼런스 및 예외로 값이 없는 상황을 가장 단순하게 구현할 수 있다고 판단했고 결과적으로 null 및 관련 예외가 탄생했다. 호어는 억만 달러짜리 실수라고 했지만 50년이라는 null 레퍼런스의 역사에 비추어볼 때 null로 인한 실질적인 피해비용은 이보다 클 수 있다.
위 코드는 조금 다른 방법으로 중첩 if 블록을 없앴다. 그러나 null일 때 반환되는 기본값 'Unknown'이 세 곳에서 반복되고 있는데 같은 문자열을 반복하면서 오타 등의 실수가 생길 수 있다.
null 때문에 발생하는 문제
자바에서 null 레퍼런스를 사용하면서 발생할 수 있는 이론적, 실용적 문제를 확인하자.
에러의 근원이다 : NullPointerException은 자바에서 가장 흔히 발생하는 에러다
코드를 어지럽힌다 : null 확인 코드를 추가해야 하므로 과도한 체크 로직으로 가독성이 떨어진다.
아무 의미가 없다 : 정적 형식 언어에서 값이 없음을 표현하는 방법으로는 적절하지 않다.
자바 철학에 위배된다
형식 시스템에 구멍을 만든다
Optional 클래스
Optional은 선택형값을 캡슐화하는 클래스다. 값이 있으면 Optional 클래스는 값을 감싼다. 반면 값이 없으면 Optional.empty 메서드로 Optional을 반환한다. 의미상으론 둘이 비슷하지만 실제로는 차이점이 많다. null을 참조하려 하면 NullPointerException이 발생하지만 Optional.empty()는 Optional 객체이므로 이를 다양한 방식으로 활용할 수 있다.
Optional 객체 생성
Optional.of
value가 null 일 경우 NPE 예외가 발생한다.
Optional<City> city = Optional.of(city);
Optional.ofNullable
value가 null인 경우 비어있는 Optional을 반환한다.
Optional<City> city = Optional.ofNullable(city);
Optional.isEmpty
비어있는 Optional 객체를 생성한다.
Optional<City> city = Optional.isEmpty();
Optional 중간처리
filter
predicate 값이 참이면 해당 필터를 통과시키고 거짓이면 비어있는 Optional 객체를 반환한다.
// 메서드 시그니처
public T orElseThrow();
// 예제 (자바 8)
Optional.ofNullable(something).orElseThrow(NoSuchElementException::new);
// 예제 (자바 10)
Optional.ofNullable(something).orElseThrow();
주의 사항
Optional을 사용하면 여러가지 상황에서 발생할 수 있는 NPE를 줄일 수 있다. 그러나 Optional은 만능 솔루션이 아니다. Optional을 잘 못 사용하면 자칫 성능이 안좋거나 가독성이 좋지 않은 반대의 상황을 직면할 수 있다. 그렇다면 어떤 경우를 주의해야 하는지 살펴보자
ifPresent(), get()을 함께 사용하는 것 보단 orElse(), orElseGet(), orElseThrow()를 사용하라
성능엔 이슈는 없지만 가독성 측면에서 역효과가 나타난다.
orElse() 대신 orElseGet()을 사용하라
orElse는 조건을 충족하지 않아도 무조건 수행되므로 자칫 불필요한 로직이 수행될 수 있다.
Optional을 필드로 사용 하지 말아라
Optional을 사용하면 직렬화가 구현되어 있지 않아 직렬화를 사용할 수 없다.
Optional을 컬렉션의 원소로 사용하지 말아라
이미 컬렉션에는 getOrDefault(), putIfAbsent(), computeIfPresnt() 등 null 체크에 대한 메소드를 제공해주므로 굳이 Optional을 변환하여 원소로 사용할 필요가 없다.