728x90
반응형
SMALL
참고자료
김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 강의 | 김영한 - 인프런
김영한 | , [사진]국내 개발 분야 누적 수강생 1위,제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다? 이걸로는 안됩니다!전 우아한형제들 기술이사, 누적 수강생 40만
www.inflearn.com
Optional이 좋아보여도, 무분별하게 사용하면 오히려 코드 가독성과 유지보수에 도움이 되지 않을 수 있다. Optional은 주로 메서드의 반환값에 대해 값이 없을 수도 있음을 표현하기 위해 도입되었다.
⭐️ 여기서 핵심은, 메서드의 반환값에 Optional을 사용하라는 것이다.
1. 필드에는 가급적 사용하지 말기
잘못된 예시
public class Address {
private Optional<String> street;
...
}
- 이렇게 되면 다음과 같은 3가지 상황이 발생한다.
- name = null
- name = Optional.empty()
- name = Optional.of(value)
- Optional 자체도 참조 타입이기 때문에, 혹시라도 개발자가 부주의로 Optional 필드에 null을 할당하면, 그 자체가 NullPointerException을 발생시킬 여지를 남긴다.
- 값이 없을수도 있음을 명시하기 위해 사용하는 것이 Optional인데 정작 필드 자체가 null이면 혼란이 가중된다.
2. 메서드 매개변수로 Optional 사용하지 말기
잘못된 예시
public void processOrder(Optional<Long> orderId) {
if (orderId.isPresent()) {
System.out.println("Order ID: " + orderId.get());
} else {
System.out.println("Order ID is empty!");
}
}
- 매개변수로 Optional을 선언하면, 결국 이게 그냥 null 처리하는 코드랑 무엇이 다른가?
- 반면, 호출하는 쪽은 그냥 processOrder(null)처럼 익숙한 코드를 호출했으면 되는거를 이렇게 코드를 작성하면 processOrder(Optional.empty())와 같은 익숙하지 않은 코드를 작성해야 한다.
3. 컬렉션이나 배열 타입을 Optional로 감싸지 말기
잘못된 예시
public Optional<List<String>> getUserRoles(String userId) {
List<String> userRolesList ...;
if (foundUser) {
return Optional.of(userRolesList);
} else {
return Optional.empty();
}
}
- 컬렉션 자체가 이미 비어있는 상태를 표현할 수 있게 설계됐다. 그래서 그냥 userRolesList.isEmpty()를 호출하면 되는거를 Optional로 감싸면, 이중으로 비어있는지를 확인해야 한다.
그리고, 논외지만 추가적으로 리스트를 반환할 때 빈 리스트를 반환해야하지, null을 반환하면 안된다. 그러니까 아래 예시를 보자.
public List<String> getList() {
if (...) {
return 리스트;
}
return null;
}
- 이 코드보면, 어떤 조건이 만족할땐 리스트를 반환하지만 그렇지 않은 경우 null을 반환한다. 근데 이게 정말 안 좋은 방식인게 이거 호출하는 쪽은 리스트를 반환한다고 하면 보통 null이 오는 것을 고려하지 않는다.
- 왜냐? 리스트는 이미 빈 값을 표현할 수 있기 때문에 값이 없으면 빈 리스트가 돌아올 것을 예상하지 null 자체를 예상하지 않는다. 그래서 이 자체로 NPE(NullPointerException)을 유발시킬 수 있다.
4. isPresent()와 get() 조합을 직접 사용하지 않기
잘못된 예시
public static void main(String[] args) {
Optional<String> optStr = Optional.ofNullable("Hello");
if (optStr.isPresent()) {
System.out.println(optStr.get());
} else {
System.out.println("Nothing");
}
}
- Optional의 get() 메서드는 가급적 사용하지 않아야 한다.
- 위 코드는 사실상 null 체크와 다를 바가 없으며, isPresent()를 깜빡하면 NoSuchElementException 같은 예외가 발생할 위험이 있다.
- 대신 orElse(), orElseGet(), orElseThrow(), ifPresentOrElse(), map(), filter() 등의 메서드를 활용하면 간결하고 안전하게 처리할 수 있다.
물론, "언제나 항상"은 없다. 위 코드처럼 get()을 사용해야 할 경우가 있을 수도 있다. 그럴땐 꼭 isPresent()와 함께 사용해야 한다.
5. orElse() vs orElseGet() 차이를 분명히 이해하기
- orElse(T other)는 항상 other를 즉시 생성하거나 계산한다. 즉, Optional 값이 존재해도 불필요한 연산, 객체 생성이 일어날 수 있다. (즉시 평가)
- orElseGet(Supplier<? extends T> supplier)는 필요할 때만(빈 Optional 일 때만) Supplier를 호출한다. 값이 이미 존재하는 경우에는 Supplier가 실행되지 않으므로, 비용이 큰 연산을 뒤로 미룰 수 있다. (지연 평가)
정리하자면, 비용이 크지 않은(또는 간단한 상수) 대체값이라면 간단하게 orElse()를 사용하자. 반면, 복잡하고 비용이 큰 객체 생성이 필요한 경우, 그리고 Optional 값이 이미 존재할 가능성이 높다면 orElseGet()을 사용하자.
6. 무조건 Optional이 좋은 것은 아니다.
다음과 같은 경우에 오히려 불필요할 수 있다.
- 항상 값이 있는 상황 - 비즈니스 로직상 null이 될 수 없는 경우, 그냥 일반 타입을 사용하거나 방어 코드로 예외를 던지는 편이 낫다.
- "값이 없으면 예외를 던지는 것"이 더 자연스러운 상황 - 예를 들어 찜질방에 A 고객이라는 유저 정보가 있는데 해당 유저의 락커가 없는 경우? 뭔가 문제가 있다.
- 흔히 비는 경우가 아니라 흔히 채워져 있는 경우
- 성능이 극도로 중요한 로우레벨 코드 - Optional은 래퍼 객체를 생성하므로, 수많은 객체가 단기간에 생겨나는 영역에서는 성능 영향을 줄 수 있다. (일반적인 비즈니스 로직에서는 문제가 되지 않는다)
728x90
반응형
LIST
'JAVA의 가장 기본이 되는 내용' 카테고리의 다른 글
병렬 스트림 제대로 이해하기 (feat. Fork/Join Framework) (0) | 2025.04.02 |
---|---|
디폴트 메서드 (0) | 2025.04.02 |
즉시 평가와 지연 평가 (0) | 2025.04.01 |
Optional - 값 획득, 값 처리 방식 (0) | 2025.04.01 |
Stream API - 컬렉터, 다운 스트림 컬렉터 (0) | 2025.03.31 |