자바 Optional orElse vs orElseGet 차이 한 번에 정리
orElse와 orElseGet의 가장 큰 차이는 기본값을 언제 만드느냐입니다. orElse는 Optional에 값이 있든 없든 인자를 항상 먼저 평가하고, orElseGet은 값이 없을 때만 Supplier를 실행합니다. 그래서 기본값 생성 비용이 크거나 DB 조회 같은 부수효과가 있으면 orElseGet을 써야 합니다. 아래에서 실제 실행 결과로 증명합니다. (Java 8+ · 예제는 Java 21 기준)
orElse와 orElseGet, 시그니처부터 다르다
공식 JavaDoc의 시그니처를 보면 둘의 성격이 드러납니다. orElse는 이미 만들어진 값(T)을 받고, orElseGet은 값을 만드는 함수(Supplier)를 받습니다.
public T orElse(T other)
public T orElseGet(Supplier<? extends T> supplier)
| 구분 | orElse(T other) | orElseGet(Supplier) |
|---|---|---|
| 인자 형태 | 이미 계산된 값 | 값을 만드는 함수 |
| 평가 시점 | 항상 (즉시·eager) | 값이 없을 때만 (지연·lazy) |
| 기본값 생성 비용 | 값이 있어도 발생 | 필요할 때만 발생 |
| 권장 상황 | 기본값이 상수·리터럴 | 기본값 생성이 무겁거나 부수효과 있음 |
코드로 확인하는 결정적 차이
기본값을 만드는 createDefault()에 로그를 넣어, 이 메서드가 언제 실제로 호출되는지 확인해 보겠습니다.
import java.util.Optional;
public class OptionalDemo {
static String createDefault() {
System.out.println(" >> createDefault() 실제로 호출됨");
return "기본값";
}
public static void main(String[] args) {
Optional<String> present = Optional.of("실제값");
System.out.println("[1] orElse - 값이 있을 때");
String a = present.orElse(createDefault());
System.out.println(" 결과: " + a);
System.out.println("[2] orElseGet - 값이 있을 때");
String b = present.orElseGet(OptionalDemo::createDefault);
System.out.println(" 결과: " + b);
Optional<String> empty = Optional.empty();
System.out.println("[3] orElseGet - 값이 없을 때");
String c = empty.orElseGet(OptionalDemo::createDefault);
System.out.println(" 결과: " + c);
}
}
실행 결과입니다.
[1] orElse - 값이 있을 때
>> createDefault() 실제로 호출됨
결과: 실제값
[2] orElseGet - 값이 있을 때
결과: 실제값
[3] orElseGet - 값이 없을 때
>> createDefault() 실제로 호출됨
결과: 기본값
핵심은 [1]과 [2]의 차이입니다. Optional에 "실제값"이 들어 있어 기본값이 필요 없는데도, orElse는 createDefault()를 호출했고([1]), orElseGet은 호출하지 않았습니다([2]). 두 방식 모두 최종 결과는 "실제값"으로 같지만, 그 값을 얻기까지 실행된 코드가 다릅니다.
orElse(x)에서 x는 메서드 인자라 자바가 메서드를 호출하기 전에 무조건 먼저 계산합니다. 반면 orElseGet(supplier)의 supplier는 Optional 내부에서 값이 없을 때만 get()이 호출됩니다.
실무에서 자주 터지는 함정
이 차이는 기본값이 상수일 때는 문제가 안 됩니다. 하지만 기본값이 DB 조회·객체 생성·API 호출이면 심각한 버그가 됩니다. 아래 코드는 값이 있어도 매번 DB를 두드립니다.
// ❌ userOptional에 값이 있어도 findDefaultUser()가 항상 실행됨 (불필요한 DB 조회)
User user = userOptional.orElse(userRepository.findDefaultUser());
// ✅ 값이 없을 때만 findDefaultUser() 실행
User user = userOptional.orElseGet(userRepository::findDefaultUser);
위 코드에서 orElse 버전은 캐시가 이미 있어도 매 요청마다 기본 사용자 조회 쿼리를 날립니다. 트래픽이 많은 API라면 그대로 성능 저하로 이어집니다. 기본값 생성에 조금이라도 비용이나 부수효과가 있다면 orElseGet이 정답입니다.
언제 orElse, 언제 orElseGet?
- orElse — 기본값이
"",0,Collections.emptyList()처럼 이미 존재하는 값·리터럴일 때. 코드가 짧고 읽기 쉽습니다. - orElseGet — 기본값을 만드는 데 연산·조회·객체 생성이 필요할 때. 값이 있을 때 그 비용을 아낍니다.
덤: get, orElseThrow도 구분하자
기본값이 아니라 "값이 없으면 예외"를 원한다면 get() 대신 orElseThrow()를 쓰는 편이 명확합니다. 둘 다 비어 있으면 NoSuchElementException을 던지지만, 의도가 이름에 드러나는 orElseThrow()가 최신 권장 방식입니다.
public T orElseThrow() // 비어 있으면 NoSuchElementException
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
두 번째 형태는 던질 예외까지 지정할 수 있습니다: userOptional.orElseThrow(() -> new UserNotFoundException(id))처럼 씁니다.
자주 묻는 질문 (FAQ)
Q. 결과 값이 똑같은데 왜 신경 써야 하나요?
반환값은 같지만 실행되는 코드가 다릅니다. 기본값 생성에 DB 조회나 부수효과가 있으면 orElse는 값이 있어도 그걸 매번 실행해 버그·성능 문제가 됩니다.
Q. 기본값이 단순 문자열이면 무엇을 써도 되나요?
네. "기본값" 같은 리터럴이면 orElse가 더 간결합니다. 생성 비용이 없어 둘의 성능 차이도 없습니다.
Q. orElseGet에 넘긴 람다가 값이 있을 때도 컴파일은 되던데요?
컴파일은 됩니다. 실행(호출)만 값이 없을 때로 미뤄지는 것이지, 람다·메서드 참조 자체는 항상 정의됩니다.
마무리
정리하면 orElse는 즉시 평가, orElseGet은 지연 평가입니다. 기본값이 상수면 orElse, 생성 비용·부수효과가 있으면 orElseGet을 쓰세요. 애매하면 orElseGet이 안전한 기본 선택입니다. 자바 표준 라이브러리에는 이렇게 무심코 쓰면 성능·정확성 문제가 생기는 API가 더 있습니다. 예를 들어 SimpleDateFormat이 thread-safe하지 않아 멀티스레드에서 날짜가 깨지는 문제도 대표적인 함정이니 함께 확인해 두면 좋습니다.
📚 참고 출처 (2026년 7월 20일 확인 · 코드는 Java 21에서 직접 실행)
· Optional (Java SE 21 API Documentation)

COMMENTS