개발할 때 개발자를 괴롭히는 예외 중 하나는 NullPointerException 입니다. NullPointerException 을 피하고 null
체크하는 로직을 줄이기 위해 빈 값일 때 null
대신 초기값을 사용하길 권장하곤 합니다.
1 | List<String> names = getNames(); |
자바 8에서는 Optional<T>
클래스를 이용해서 NullPointerException 을 방지할 수 있습니다. Optional<T>
클래스는 한 마디로 null
이 올 수 있는 값을 감싸는 래퍼 클래스로 참조하더라도 null
이 일어나지 않도록 해주는 클래스입니다.
1 | public final class Optional<T> { |
위 코드의 value
에 값을 저장하기 때문에 값이 null
이더라도 바로 참조 시 NPE 가 나지 않고, 클래스이기 때문에 각종 메소드를 제공해줍니다.
생성하기
먼저 다음과 같이 빈 객체를 생성할 수 있습니다.
1 | Optional<String> optional = Optional.empty(); |
혹은 null
이 올 수 있는 값을 Optional<T>
로 감싸서 생성할 수 있습니다. orElse
또는 orElseGet
메소드를 이용해서 값이 없는 경우라도 안전하게 값을 가져올 수 있습니다.
다음 예제에서는 getString
메소드가 리턴하는 값이 null
일 수도 있습니다. 하지만 Optional<T>
로 감싸면 됩니다.
1 | // Optional 안에는 값이 있을 수도 있고 빈 객체일 수도 있다. |
사용하기
사용법은 간단합니다. 먼저 자바 8 이전에는 다음과 같이 null
체크가 필요했습니다.
1 | // 자바 8 이전 |
Optional<T>
과 Lambda 를 이용하면 좀 더 간단하게 표현할 수 있습니다.
1 | List<String> listOpt = Optional.ofNullable(getList()).orElseGet(() -> new ArrayList<>()); |
다음 코드는 null
체크 때문에 지저분해진 코드입니다.
1 | User user = getUser(); |
map
메소드는 해당 값이 null
이 아니면 mapper 를 이용해 계산한 값을 저장하는 Optional 객체를 리턴합니다. 만약 값이 null
이라면 빈 Optional 객체를 리턴합니다.
1 | public<U> Optional<U> map(Function<? super T, ? extends U> mapper) |
map
메소드를 이용해서 간단하게 표현해보겠습니다.
1 | Optional<User> user = Optional.ofNullable(getUser()); |
만약 위 예제에서 getAddress
와 getStreet
가 Optional<T>
객체를 리턴한다면 flatMap
메소드를 사용할 수도 있습니다.
1 | Optional<User> user = Optional.ofNullable(getUser()); |
다음 예제는 NullPointerException 을 핸들링하는 예제입니다.
1 | String value = null; |
위와 같은 코드는 Optional<T>
을 이용하면 다음과 같이 표현할 수 있습니다.
1 | String value = null; |
논란거리
옵셔널은 논란이 있는 클래스입니다. NPE 를 더 쉽게 다룰 수 있고, 다른 포스트에서 살펴볼 스트림과 함께 사용하면 간결하게 코딩할 수 있는 장점이 있습니다. 하지만 처음부터 Option
을 지원한 Scala 와 다르게 이미 많은 코드가 옵셔널 없이 null
체크를 하고 있기 때문에 개발자를 혼란스럽게 하고 기존 소스와 다른 코드 스타일을 만들 수 있어 부정적으로 보는 개발자들도 많습니다.
모든 도구가 그렇듯 장점과 단점이 있고, 쓰기 나름일 겁니다. 기존 프로젝트는 기존 코딩 스타일을 유지하되, 새롭게 시작하는 프로젝트에서는 적용해보는 것도 좋을 것 같습니다.