Map.getOrDefault()를 사용하다가 Exception을 만난 경험이 있다면, 이 글이 도움이 될 수 있습니다.
많은 사람들이 "getOrDefault는 키가 없을 때만 기본값을 사용한다"라고 생각하지만, 실제 동작은 다릅니다.
이 글에서는 getOrDefault()의 진짜 동작 방식을 알아보고, 안전한 대안까지 정리해 보겠습니다. 👩🏻💻
예상 못한 예외, 어디서부터 잘못된 걸까?
다음 코드에서 NumberFormatException이 발생했습니다.
Long no = noMap.getOrDefault(update.getNo(), Long.valueOf(update.getNo()));
에러 메시지는 아래와 같았습니다.
java.lang.NumberFormatException: For input string: "qwer1234"
noMap에 키가 존재하지 않으면 update.getNo()를 Long으로 바꿔서 기본값으로 사용하려던 코드였습니다. 그런데 값이 "qwer1234"처럼 UUID 형태의 문자열이라면, Long.valueOf(...)에서 바로 터지게 됩니다...😥
getOrDefault()는 "게으르지 않다"
많은 개발자들이 getOrDefault()를 다음과 같은 의미로 이해하곤 합니다.
“키가 없을 때만 기본값을 사용한다.”
하지만 실제 동작 방식은 그렇지 않습니다. 기본값으로 전달한 인자도 무조건 먼저 평가됩니다..
즉, 이 코드는
Long.valueOf(update.getParentNo()) // 먼저 실행됨!
이 실행된 결과를 getOrDefault의 두 번째 인자로 넘깁니다.
키가 존재하든 말든 Long.valueOf()는 실행됩니다!
getOrDefault() 내부 구현은??
내부적으로도 defaultValue는 평가가 완료된 상태로 들어갑니다. 즉, 함수나 람다처럼 "필요할 때 실행"되는 구조가 아닙니다.
안전한 대안 ✅
조건문으로 명확하게 분기 처리하는 게 가장 안전합니다.
Long no;
if (noMap.containsKey(update.getNo())) {
no = noMap.get(update.getNo());
} else {
no = Long.valueOf(update.getNo());
}
또는 함수형 스타일이 익숙하다면
Long no = Optional.ofNullable(noMap.get(update.getNo()))
.orElseGet(() -> Long.valueOf(update.getNo()));
orElseGet()은 람다를 필요할 때만 실행하므로 Long.valueOf(...) 호출이 지연됩니다.
Java의 Map.getOrDefault()는 게으른(Lazy) 친구가 아닙니다.
기본값도 무조건 실행하므로, 부작용 가능성이 있는 코드는 절대 직접 넣지 말아야 합니다.
특히 다음과 같은 경우는 꼭 분기 처리하거나 orElseGet을 사용합시다!
- 숫자 파싱 (Long.valueOf, Integer.parseInt)
- 외부 API 호출
- I/O 또는 DB 접근
- 무거운 연산
'Java' 카테고리의 다른 글
JDK 23: 자바 핵심 변경점 정리 (0) | 2025.01.15 |
---|---|
Jackson 커스텀 어노테이션: @JacksonAnnotationsInside (1) | 2024.12.21 |
[Java] JPA에서 발생하는 N+1 이슈: 원인부터 해결까지 (1) | 2024.11.27 |
Java의 Reflection 사용법과 주의점 (0) | 2024.11.24 |
[Java] 빌더 패턴 (1) | 2024.11.23 |