옵셔널 반환은 신중히 하라
자바 8 전에는 메서드가 특정 조건에서 값을 반환할 수 없을 때 예외를 던지거나, null을 반환했다. 두 방법 모두 허점이 있다. 예외는 진짜 예외적인 상황에서만 사용해야 하며 예외를 생성할 때 스택 추적 전체를 캡처하므로 비용도 많이 든다. null 반환은 별도의 null 처리 코드를 추가해야 한다.
Optional<T>
null이 아닌 T 타입 참조를 하나 담거나, 혹은 아무것도 담지 않을 수 있다. 아무것도 담지 않은 옵셔널은 비었다고 말한다. 옵셔널은 원소를 최대 1개 가질 수 있는 ‘불변’ 컬렉션이다.
보통은 T를 반환해야 하지만 특정 조건에서는 아무것도 반환하지 않아야 할 때 T 대신 Optional<T>
를 반환하도록 선언하면 된다. 그러면 유효한 반환값이 없을 때는 빈 결과를 반환하는 메서드가 만들어진다.
옵셔널을 반환하는 메서드는 예외를 던지는 메서드보다 유연하고 사용하기 쉬우며, null을 반환하는 메서드보다 오류 가능성이 작다.
예외를 던지는 메서드
public static <E extends Comparable<E>> E max(Collection<E> c) {
if (c.isEmpty())
throw new IllegalArgumentException("빈 컬렉션");
E result = null;
for (E e : c)
if (result == null || e.compareTo(result) > 0)
result = Objects.requireNonNull(e);
return result;
}
Optional<E> 반환하는 메서드
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.ofNullable(value)
를 사용하면 된다. 옵셔널을 반환하는 메서드에서는 절대 null을 반환하지 말자.
스트림 버전
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
return c.stream().max(Comparator.naturalOrder());
}
옵셔널은 검사 예외와 취지가 비슷하다. 즉, 반환값이 없을 수도 있음을 API 사용자에게 명확히 알려준다. 비검사 예외를 던지거나 null을 반환한다면 API 사용자가 그 사실을 인지하지 못해 끔찍한 결과로 이어질 수 있다. 하지만 검사 예외를 던지면 클라이언트에서는 반드시 이에 대처하는 코드를 작성해넣어야 한다.
메서드가 옵셔널을 반환한다면 클라이언트는 값을 받지 못했을 때 취할 행동을 선택해야 한다.
옵셔널 사용 시 고려사항
-
결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야 할 때
Optional<T>
을 반환한다. -
컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안 된다. 빈
Optional<List<T>>
를 반환하기보다는 빈List<T>
를 반환하는 게 좋다. 빈 컨테이너를 그대로 반환하면 클라이언트에 옵셔널 처리 코드를 넣지 않아도 된다. -
Optional도 엄연히 새로 할당하고 초기화해야 하는 객체이고, 그 안에서 값을 꺼내려면 메서드를 호출해야 한다. 그래서 성능이 중요한 상황에서는 옵셔널이 맞지 않을 수 있다.
-
박싱된 기본 타입을 담는 옵셔널은 기본 타입 자체보다 무거울 수 밖에 없다. 그래서 자바 API 설계자는 int, long, double 전용 옵셔널 클래스들을 만들었다.
OptionalInt, OptionalLong, OptionalDouble
이다. 박싱된 기본 타입을 담은 옵셔널을 반환하는 일은 없도록 하자. Boolean, Byte, Character, Short, Float은 예외다. -
옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는 게 적절한 상황은 거의 없다.
-
인스털스 필드 중 필수가 아니고 기본 타입이라 값이 없음을 나타낼 방법이 마땅치 않은 경우, 필드 자체를 옵셔널을 선언할 수 있다.
값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환값이 없을 가능성을 염두에 둬야 하는 메서드라면 옵셔널을 반환해야 할 상황일 수 있다. 하지만 옵셔널 반환에는 성능 저하가 뒤따르니, 성능에 민감한 메서드라면 null을 반환하거나 예외를 던지는 편이 낫다. 그리고 옵셔널을 반환값 이외의 용도로 쓰는 경우는 매우 드물다.
Comments