[Effective Java] 옵셔널 반환은 신중히 하라

Optional

자바8 부터 Optional을 지원하기 시작했다. Optional은 다음과 같은 시나리오에서 쓰인다. 값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환값이 없을 가능성을 염두에 둬야하는 메소드를 작성할 때 사용한다. Optional을 사용한다면 클라이언트측 코드에 값이 반환되었는지를 알리는 형태가 되니 원인 메소드와 멀리 떨어진 코드에서 NullPointerException이 발생하는 경우를 예방할 수 있다. 다음은 Optional을 사용한 코드이다.

1
2
3
4
5
6
7
8
9
10
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
if(c.isEmpty())
return Optional.empty();

E result = null;
for(E e : c)
if(result == null || e.compareTo(result) > 0)
result = Objects.requireNonNull(e);
return Optional.of(result);
}

Optional.of(value)는 null을 넣으면 NullPointerException이 발생하니 주의하자. 만약 null을 허용하는 Optional을 만들고 싶다면 Optional.ofNullable(value)를 사용하자.

스트림 종단 연산중 상당수가 Optional을 반환한다. 다음은 위 코드를 스트림으로 표현한 코드이다.

1
2
3
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
return c.stream().max(Comparator.nuturalOrder());
}

Optional의 장점은 클라이언트 측에 값이 없을수도 있다는 것을 알리는 것이다. 그래서 클라이언트 측은 값이 없을 때 처리하는 로직을 작성해야 한다. 다음은 클라이언트 측의 코드 예이다.

1
2
3
4
5
6
7
8
//기본 값 설정
String lastWordInLexicon = max(words).orElse("no words");

//예외 던지기
String lastWordInLexicon = max(words).orElseThrow(fooException::new);

//반드시 값이 있다고 확신할 수 있다면 다음과 같이 바로 get할 수 있다.
String lastWordInLexicon = max(words).get();

이따끔 기본값을 생성하는데 비용이 큰 경우가 있다. 그럴때는 Supplier<T>를 인수로 받는 orElseGet을 사용하면 된다. Supplier<T>는 lazy하니 실제 값 사용시에 생성을 하므로 초기 생성 비용을 낮출 수 있다. 더 특별한 쓰임이 필요하다면 filter, map, flatMap, ifPresent를 사용하면 된다.

스트림을 사용한다면 옵셔널들을 Stream<Optional<T>>로 받아서 그 중 채워진 옵셔널들에서 값을 뽑다 Stream<T>에 건네는 경우도 있다. 다음 코드는 그 예이다.

1
streamOfOptionals.filter(Optional::isPresent).map(Optional::get);

컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸지 말고 빈 컨테이너를 리턴하자. Optional을 사용하는데도 대가가 따른다. Optional도 초기화해야하는 객체이고 그 안에서 값을 꺼내려면 메소드 콜 스택이 한 단계 더 깊어지는 것이다. 그래서 성능이 중요한 곳이라면 옵셔널이 맞지 않을때도 있다. 또한 박싱된 기본 타입을 담는 옵셔널은 무거울 수 밖에 없다. 그럴 때를 위해 OptionalInt, OptionalDouble, OptionalLong을 사용하자.

Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/18/Languages/Effective%20JAVA/item55/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.