옵셔널 반환은 신중히 하라
자바 8 이전에는 값을 반환할 수 없을 때 (1) 예외 던지기, (2) null 반환 방식이 있었다. (1)의 경우, 진짜 예외적인 상황에서만 사용해야 하고 예외를 생성할 때 스택 추적 전체를 캡처해야 하므로 비용이 만만치 않다. (2)의 경우는 클라에서 별도의 null 처리 코드를 추가해야 하고 만약 무시한다면 NPE가 터질 수 있다.
자바8에서는 Optinal<T>
이 생기게 되었다. 옵셔널을 반환하는 메서드는 예외를 던지는 메서드보다 유연하고 사용하기 쉬우며, null을 반환하는 메서드보다 오류 가능성이 적다.
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로 반환한다면, 클라에서 취할 수 있는 방법은 다음과 같다.
1) 기본값 정하기
String lastWordInLexicon = max(words).orElse("단어없음");
2) 원하는 예외 던지기
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
3) 항상 값이 채워져 있다고 가정
Element lastNobleGas = max(Elements.NOBLE_GASES).get();
4) isPresent
"Parent PID: " + (parentProcess.isPresent() ? String.valueOf(parentProcess.get().pid()) : "N/A")
5) map
ph.parent().map(h -> String.valueOf(h.pid())).orElse("N/A"));
6) 스트림 filter
streamOfOptionals
.filter(Optional::isPresent)
.map(Optional::get)
7) flatMap
streamOfOptionals
.flatMap(Optional::stream)
1) 컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안된다.
또다시 옵셔널에 대한 처리를 클라에서 해야한다.
2) 결과가 없을 수 있고, 클라가 이 상황을 특별히 사용해야 한다면 Optional<T>
사용
Optional을 새로 할당하고 초기화하고 그 안에서 값을 꺼내려면 한 단계가 더 필요하다. 따라서 성능이 중요하다면 옵셔널이 맞지 않을 수 있다.
3) 박싱된 기본 타입을 담은 옵셔널을 반환하는 일은 없도록 하자
int, long, double 전용 옵셔널 클래스인 OptionalInt, OptionalLong, OptionalDouble이 존재 한다.
4) Map의 키로 쓰지말자
map의 키로 쓴다면 맵 안에 키가 없다는 사실을 나타내는 경우가 두 가지가 된다. (1) 키 자체가 없는 경우, (2) 키가 있지만 그 키 속이 빈 옵셔널인 경우.
더 일반화해서, 옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는 게 적절한 상황은 거의 없다.
반환 값이 없는 메서드라면 옵셔널을 반환해야할 수도 있다. 하지만 옵셔널 반환에는 성능 저하가 뒤따르므로 null이나 예외를 던지는 편이 나을 수 있다. 그리고 옵셔널을 반환값 이외의 용도로 쓰는 경우는 매우 드물다!