자바 8이전에는 메서드가 특정 조건을 반환할 수 없을 때
을 했다. 하지만 각각 문제점이 있다.
자바 8부터는 Optional<T>
가 생겼다. null이 아닌 T타입 참조 하나를 담거나, 혹은 아무것도 담지 않을 수도 있다. 옵셔널은 원소를 최대 1개 가질 수 있는 '불변' 컬렉션이다.
보통은 T를 반환해야 하지만 특정 조건에서는 아무것도 반환하지 않아야할 때 T 대신 Optional<T>
를 반환하도록 선언하면된다. 옵셔널을 반환하는 메서드는 예외를 던지는 메서드보다 유연하고 사용하기 쉬우며, null을 반환하는 메서드보다 오류 가능성이 작다.
컬렉션에서 최대값을 구해 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) > )
result = Objects.reuqireNonNull(e);
return Optional.of(result);
}
빈 옵셔널은 Optional.empty()
로 만들고, 값이 든 옵셔널은 Optional.of(value)
로 생성했다. Optional.of(value)
에 null을 넣으면 NullPointerException
을 던지니 주의하자. null 값도 허용하는 옵셔널을 만들려면 Optional.ofNullable(value)
를 사용하면 된다. 옵셔널을 반환하는 메서드에서는 절대 null을 반환하지말자. 옵셔널을 쓸 이유가 없다.
null 반환이나 예외 반환 대신 옵셔널 쓰는 이유는 반환 값이 없을 수도 있음을 API 사용자에게 명확히 알려준다라는 점이다.
메서드가 옵셔널을 반환한다면 클라이언트는 값을 받지 못했을 때 취할 행동을 선택해야 한다. 그중 하나는 기본값을 설정하는 방법이다.
String lastWordInLexicon = max(words).orElse("단어 없음...");
상황에 맞는 예외를 던질수도 있는데, 아래 코드에서 실제 예외가 아니라 예외 팩터리를 건내고 있다. 이러면 예외가 실제로 발생하지 않는 한 예외 생성 비용은 들지 않는다.
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
기본값을 설정하는 비용이 부담스럽다면 Supplier<T>
를 인수로 받는 orElseGet
을 사용하면 값이 처음 필요할 때 Supplier<T>
를 사용해 생성하므로 초기 설정 비용을 낮출 수 있다.
isPresent
메서드는 옵셔널이 채워져있으면 true를, 비워져있으면 false를 반환한다. 하지만 앞의 다른 메서드들로 거의 대처할 수 있으므로 신중히 쓰자.
항상 옵셔널을 써야하는건 절대 안디ㅏ. 컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안된다. 빈 Optional<List<T>>>
를 반환하기보다는 빈 List<T>
를 반환하는게 좋다.
결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야 한다면 Optional<T>
를 반환한다.
Optional도 엄연히 새로 할당, 초기화해야 하는 객체이고, 그 안에서 값을 꺼내려면 메서드를 호출해야하니 성능이 중요한 상황이면 안 맞을 수도 있다.
박싱된 기본 타입을 담는 옵셔널은 기본 타입 자체보다 무겁다. 그래서 OptionalInt
, OptionalLong
, OptionalDouble
과 같은 것들이 있으니 박싱된 기본 타입을 담은 옵셔널을 반환하지 말자.
옵셔널은 맵의 값으로 사용하면 절대 안된다. 그러면 키가 없다는 사실을 나타내는 경우가 두 가지가 된다. 하나는 키 자체가 없거나 다른 하나는 키는 있지만 속이 빈 옵셔널인 경우다. 그래서 옵셔널을 컬렉션의 키, 값, 원소내 배열의 원소로 사용하는게 적절한 상황은 거의 없다.