반응형
이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.
아이템 55. 옵셔널 반환은 신중히 하라
메서드가 특정 조건에서 값을 반환할 수 없을 때 처리하는 방법
~ 자바 7
- 예외를 던지거나, (반환 타입이 객체 참조라면) null을 반환
- 예외는 진짜 예외적인 상황에서만 사용해야 하며 예외를 생성할 때는 스택 추적 전체를 캡처하므로 비용이 만만치 않다.
- null을 반환하면 이런 문제가 생기진 않지만, 별도의 null 처리 코드를 추가해야 한다.
자바 8 이후의 새로운 방법 : Optional<T>
- Optional<T>는 null 이 아닌 T 타입 참조를 하나 담거나, 혹은 아무것도 담지 않을 수 있다.
- 아무것도 담지 않은 Optional은 ‘비었다’ 라고 한다.
- 반대로 어떤 값을 담은 Optional은 ‘비지 않았다’ 라고 한다.
- Optional은 원소를 최대 1개 가질 수 있는 ‘불변’ 컬렉션이다.
- 보통은 T를 반환해야 하지만 특정조건에서는 아무것도 반환하지 말아야할 때 T 대신 Optional<T>를 반환하도록 선언하면 된다.
- Optional을 반환하는 메서드는 예외를 던지는 메서드보다 유연하고 사용하기 쉬우며, null을 반환하는 메서드보다 오류 가능성이 작다.
ex 1) 컬렉션에서 최댓값을 구하는 예(컬렉션이 비어있는 경우 예외를 던짐)
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;
}
ex 2) 컬렉션에서 최댓값을 구해 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을 반환하는 법은 적절한 정적 팩터리를 사용해 Optional을 생성해주기만 하면 된다.
- 이 코드에서는 두 가지 팩터리를 사용했다.
- 빈 Optional은 Optional.empty()로 만들고,
- 값이 든 Optional.of(value)로 생성했다
- 참고로, Optional.of(value)에 null을 넣으면 NPE가 발생하니 주의해야 한다.
- Optional을 반환하는 메서드에는 절대 null을 반환하지 말자.
ex 3) ex 2의 코드를 스트림 버전으로 작성한 예
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
return c.stream().max(Comparator.naturalOrder());
}
- 이렇게 스트림 버전으로 작성하면 Stream의 max 연산이 필요한 Optional을 생성해준다. (비교자를 명시적으로 전달해야함)
- Optional은 검사 예외와 취지가 비슷하다.
- 즉, 반환값이 없을 수도 있음을 API를 사용하는 사람에 명확히 알려준다.
- 메서드가 Optional을 반환한다면 클라이언트는 값을 받지 못했을 때 취할 행동을 선택해야 한다.
ex 4) 기본값을 사용하는 예
String lastWordInLexicon = max(words).orElse("단어 없음...");
- 기본값을 설정하는 비용이 부담이 될 때가 있따.
- 그럴 때는 Supplier<T>를 인수로 받는 orElseGet을 사용하면, 값이 처음 필요할 때 Supplier<T>를 사용해 생성하므로 초기 설정 비용을 낮출 수 있다.
ex 5) 원하는 예외를 던지는 예
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
ex 6) Optional에 항상 값이 있다고 확신하여 바로 꺼내서 사용하는 예
Element lastNobleGas = max(Elements.NOBLE_GASES).get();
isPresent Method
- 안전 밸브 역할의 메서드로, Optional에 값이 있으면 true를, 비어있으면 false를 반환
- 이 메서드로 원하는 모든 작업을 수행할 수 있지만, 신중히 수행해야 한다.
Optional 유의점
- 반환값으로 Optional을 사용한다고해서 항상 좋은 것은 아니다.
- 컬렉션, 스트림, 배열, Optional 값은 컨테이너 타입은 Optionalfh 감싸면 안된다.
- 빈 Optional<List<T>>를 반환하기보다 빈 List<T>를 반환하는게 좋다. (Optional 처리 코드를 넣지 않아도 되므로)
- 어떤 경우에 메서드 반환 타입을 T 대신 Optional<T>로 해야할까? 결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야할 때 Optional<T>를 반환한다.
- 박싱된 기본 타입들을 Optional에 담아 반환하는 일이 없어야 한다. 대신 아래의 전용 Optional 을 사용하자
- 박싱된 기본 타입을 담는 Optional : OptinalInt, OptionalLong, OptionalDouble
- Optional을 컬렉션의 카, 값, 원소나 배열의 원소로 사용하는 게 적절한 상황은 거의 없다.
정리
- 값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환값이 없을 가능성을 염두해야한다면 Optional을 반환해야할 상황일 수도 있다.
- Optional 반환에는 성능 저하가 발생할 수 있으니, 성능에 민감하다면 null을 반환하거나 예외를 던지는 게 나을수도 있다.
- Optional을 반환값 이외의 용도로 사용하는 경우는 매우 드물다.
반응형
'BE > Java' 카테고리의 다른 글
[Effective Java] 아이템 57. 지역변수의 범위를 최소화하라 (0) | 2022.11.22 |
---|---|
[Effective Java] 아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라 (0) | 2022.11.21 |
[Effective Java] 아이템 54. null이 아닌, 빈 컬렉션이나 배열을 반환하라 (0) | 2022.11.17 |
[Effective Java] 아이템 53. 가변인수는 신중히 사용하라 (0) | 2022.11.16 |
[Effective Java] 아이템 52. 다중정의는 신중히 사용하라 (0) | 2022.11.15 |