자바 8전에는 메서드가 특정 조건에서 값을 반환할 수 없을 때 취할 수 있는 선택지는 두가지가 존재했다.
- 예외를 던진다.
- null을 반환한다.
하지만 두가지 모두 허점이 존재하게 되는데,
예외는 진짜 예외적인 상황에서만 사용해야 하며, 예외를 생성할 때 스택 추적 전체를 캡처하므로 비용도 만만치 않다. null을 반환하면 이런 문제가 생기지는 않지만, 메서드를 호출 하는 쪽에서 별도의 null 처리 코드를 추가해야하는 문제가 발생한다.
자바가 8버전으로 올라가면서 또 하나의 선택지가 생겼는데 바로 Optional<T>를 사용하는 것이다.
Optional은 null이 아닌 T 타입 참조를 하나 담거나, 혹은 아무것도 담지 않을 수 있다.
보통 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> maxOptional(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);
}
옵셔널을 반환하는 메서드에서는 절대 null을 반환하지 말자!!
2019/11/25 - [프로그래밍 노트/JAVA] - [JAVA] Optional 사용해서 null-safety한 코드짜기
스트림 버전으로 다시 작성하면 Stream의 max 연산이 우리에게 필요한 옵셔널을 생성해줄 것이다.
public static <E extends Comparable<E>> Optional<E> maxOptionalStream(Collection<E> c){
return c.stream().max(Comparator.naturalOrder());
}
그렇다면 null을 반환하거나 예외를 던지는 대신 옵셔널 반환을 선택해야하는 기준은 무엇일까?
옵셔널은 검사 예외와 취지가 비슷하다. 즉, 반환값이 없을 수도 있음을 API 사용자에게 명확히 알려준다. 비검사 예외를 던지거나 null을 반환한다면 API 사용자가 그 사실을 인지하지 못해 끔찍한 결과로 이어질 수 있다. 검사 예외를 던지면 클라이언트에서는 반드시 이에 대처하는 코드를 작성해서 넣어야 한다.
결론
- 컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안 된다.
- 결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야 한다면 Optional를 반환하자.
- 박싱된 기본 타입을 담은 옵셔널을 반환하는 일은 없도록 하자.
=> Optional<T>에 박싱된 기본타입을 담으면 성능이 느리며, OptionalInt, OptionalLong, OptionalDouble 클래스를 제공하므로 사용하자.
값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환값이 없을 가능성을 염두에 둬야하는 메서드라면 옵셔널을 반환해야 할 상황일 수 있다. 하지만 옵셔널 반환에는 성능 저하가 뒤따르니, 성능에 민감한 메서드라면 null을 반환하거나 예외를 던지는 편이 나을 수 있다. 그리고 옵셔널을 반환값 이외의 용도로 쓰는 경우는 매우 드물다.
'프로그래밍 노트 > Effective 시리즈' 카테고리의 다른 글
생성자 대신 정적 팩토리 메서드를 고려해보자. (0) | 2020.01.18 |
---|---|
자원을 직접 명시하지 말고 의존 객체 주입을 사용하자 (0) | 2020.01.18 |
메서드 시그니처를 신중히 설계하자 (API 설계 요령) (0) | 2020.01.12 |
자바 명명규칙 (Naming Rule, Naming Convention) 컨벤션 (0) | 2020.01.12 |
매개변수가 유효한지 검사하자 (0) | 2020.01.09 |