item 55 : 옵셔널 반환은 신중히 하라
1. null 반환의 문제
자바 8 이전에는 메서드가 특정 조건에서 값을 반환할 수 없는 경우 두 가지 선택지가 있었다:
예외를 던지기: 예외는 진짜 예외적인 상황에서 사용해야 하며, 스택 추적 정보를 캡처하는 비용이 크기 때문에, 자주 사용하기에는 부적절하다.
null 반환: null을 반환하는 경우, 메서드 호출 후 항상 null 체크를 해야 하며, 이를 누락하면 NullPointerException(NPE)을 발생시킬 위험이 크다.
null 반환 시 클라이언트는 메서드가 null을 반환할 가능성을 염두에 두고 코드를 작성해야 한다:
이와 같은 null 체크는 코드의 복잡성을 증가시키며, 진짜 예외 처리에 집중할 수 없게 만든다.
2. Optional의 등장
자바 8부터 Optional 클래스가 등장하여 null 반환 대신 사용할 수 있는 새로운 대안을 제공한다.
Optional<T>의 등장
Optional은 최대 1개의 원소를 담을 수 있는 불변 컬렉션으로 볼 수 있다. null 대신 비었음을 명확하게 표현할 수 있으며, 메서드 사용 시 값이 없을 수 있음을 명확히 전달한다.
null
이 아닌T
타입 참조 하나를 담거나 아무것도 담지 않는다.null
을 피하기 위한Optional
에null
을 담는 것은 안티 패턴이다.
Optional<T>
는 원소를 최대 1개 가질 수 있는 '불변 컬렉션' 이다.예외를 던지는 메서드보다 유연하고 사용하기 쉽다.
null
을 반환하는 메서드보다 오류 가능성이 적다.
3. Optional을 활용한 코드 개선
예제 1: 최대값 찾기 메서드
null을 사용한 기존 코드:
max()
메서드는 주어진 컬렉션에서 최대값을 찾는다.컬렉션이 비어 있으면 IllegalArgumentException을 던지며, 이는 호출자가 반드시 예외 처리를 해야 하는 부담을 준다.
null 체크와 예외 처리가 필요하여 코드가 복잡해진다.
Optional을 사용한 코드:
컬렉션이 비어 있으면
Optional.empty()
를 반환하여 null 반환으로 인한 NPE 문제를 피한다.Optional.of(result)
는 결과가 null이 아님을 보장한다.Optional을 사용하면 클라이언트 코드에서 null 체크 없이 Optional의 메서드들을 통해 안전하게 값을 처리할 수 있다.
예제 2: Stream과 Optional 활용
Stream API와 Optional을 이용한 코드:
Stream API를 사용하여 컬렉션에서 최대값을 찾는다.
max()
메서드는 Comparator를 사용하여 스트림에서 최대값을 계산하며, 결과를 Optional로 반환다.코드의 간결함과 가독성을 높이는 동시에, null 처리 문제도 피할 수 있다.
4. Optional 사용법 및 예제
1) 기본 값 정하기 (optional.orElse()
)
optional.orElse()
)기본 값을 반환하는 예제:
부가 설명:
orElse()
메서드를 사용하여, Optional이 비어 있을 경우 기본 값을 반환한다.이 경우 최대값이 없으면 기본 값으로 0을 반환한다.
2) 기본 예외 던지기 (optional.orElseThrow()
)
optional.orElseThrow()
)예외를 던지는 예제:
부가 설명:
orElseThrow()
메서드를 사용하여 값이 없을 때 예외를 던진다.이 경우, 빈 컬렉션이라면 IllegalArgumentException이 발생다.
3) 항상 값이 있다고 가정하기 (optional.get()
)
optional.get()
)값이 반드시 있다고 가정하는 예제:
부가 설명:
get()
메서드는 값이 반드시 있다고 가정하고 사용힌다.값이 없을 경우
NoSuchElementException
이 발생하므로 사용 시 주의
4) 초기 생성 비용이 큰 경우 (optional.orElseGet()
)
optional.orElseGet()
)초기 생성 비용을 줄이기 위한 예제:
부가 설명:
orElseGet()
은 값이 필요할 때 지연 초기화 방식으로Supplier
를 사용하여 생성한다.초기 생성 비용이 큰 객체를 필요할 때만 생성하도록 하여 비용을 줄일 수 있다.
5. Optional 사용 시 주의사항
Optional 사용을 피해야 하는 경우:
컨테이너 타입 (예:
List
,Stream
, 배열)에 Optional을 사용하지 말 것.예를 들어,
Optional<List<T>>
대신 빈 리스트를 반환하는 것이 더 간결하고 명확
박싱된 기본 타입 (예:
Integer
)을 Optional로 감싸지 말고, 전용 클래스 (OptionalInt
,OptionalLong
,OptionalDouble
)를 사용할 것.기본 타입을 박싱하면 성능 저하가 발생하므로 전용 클래스를 사용하는 것이 더 효율적
Optional을 컬렉션의 키, 값, 배열의 원소로 사용하지 말 것.
Optional을 맵의 값으로 사용하면 맵에 없는 키와 값이 빈 Optional인 경우를 구분해야 하므로 복잡성이 증가
📚 핵심 정리
반환값이 없을 가능성이 있는 메서드라면 Optional을 반환하는 것이 좋다.
이를 통해 반환값이 없을 가능성을 명확히 알리고, null 처리를 통한 NPE 위험을 줄일 수 있다.
그러나 Optional 반환에도 객체 생성 비용이 추가되므로, 성능이 중요한 상황에서는 null 반환이나 예외 처리가 더 적합할 수 있다.
Optional은 반환값 이외의 용도로는 사용하지 않는 것이 권장되며, 특히 컨테이너 타입을 감싸는 것은 지양해야 한다.
Optional을 적절히 사용함으로써 코드의 안전성과 가독성을 높일 수 있지만, 사용 시 주의할 점도 많으므로 상황에 따라 신중하게 선택
Last updated