Optional은 Java 8부터 추가된 null을 명시적으로 다루기 위한 컨테이너 클래스다.
즉, 값이 있을 수도 있고 없을 수도 있는 객체를 감싸서 NullPointerException을 방지하고, 더 읽기 쉬운 코드를 만들 수 있도록 도와준다.
가장 큰 장점이 이 값이 값이 있을 수도 있고, 없을 수도 있다는 걸 명시적으로 보여준다는 점이다. 그렇게 함으로써 NullPointerException을 방지할 수 있다. 다만, 반드시 값이 있는 경우에는 쓰지 말아야 한다.
Optional<String> name = Optional.of("철수");
값이 절대 null이 아니라고 확신할 때 사용
만약 null을 넣으면 예외 발생
Optional<String> name = Optional.of("홍길동"); // ✅
Optional<String> name = Optional.of(null); // ❌ NullPointerException
Optional<String> name = Optional.ofNullable(null); // ✅ 빈 Optional 생성
Optional<String> empty = Optional.empty();
Optional<String> name = Optional.of("철수");
| 메서드 | 설명 |
|---|---|
get() | 값을 가져옴 (값이 없으면 예외 발생), 잘 사용하지 않음. 주로 orElse나 orElseXxx를 사용함 |
isPresent() | 값이 존재하면 true |
orElse(T other) | 값이 없으면 다른 기본값 반환 |
orElseGet(Supplier) | 값이 없으면 함수 실행 후 반환 |
orElseThrow() | 값이 없으면 예외 발생 |
or(Supplier<? extends Optional<? extends T>> supplier) | 값이 없으면 Supplier가 제공하는 다른 Optional을 반환. 있어도 그 값의 Optional로 반환. |
Optional<String> name = Optional.ofNullable(null);
// 1. 기본값 제공
String result = name.orElse("기본값");
// 2. 값이 있으면 출력
name.ifPresent(System.out::println);
// 3. 값이 없으면 예외 발생
name.orElseThrow(() -> new IllegalArgumentException("값이 없음"));
둘의 가장 큰 차이는 orElse는 즉시 평가, orElseGet은 지연 평가라는 점이다. orElse는 즉시 평가라서 메서드가 호출되기 전에 괄호 안의 값이 먼저 평가되지만, orElseGet은 람다를 사용하기 때문에 지연 평가가 되어 그 값이 필요할 때까지 실행되지 않아 불필요한 연산을 사용하지 않을 수 있다. 따라서 간단한 대체 값이 쓰일 경우에는 orElse가 맞지만, 복잡한 로직 등의 상대벅으로 무거운 대체값이 쓰이거나 대체로 값이 있을 경우에는 orElseGet을 쓰는 게 맞다.
| 메서드 | 설명 |
|---|---|
ifPresent(Consumer<? super T> action) | 값이 있을 때만 action 실행 |
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) | 값이 있으면 action, 없으면 emptyAction 실행 |
map(Function<? super T, ? extends U> mapper) | 있으면 변환해서 Optinal로 반환, 없으면 empty 반환 |
flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) | map과 유사하지만, 반환할 때 평탄화해서 반환 |
filter(Predicate<? super T> predicate) | 값이 조건에 만족하면 그대로 반환, 없거나 조건에 만족하지 않으면 empty 반환 |
stream() | 있으면 단일 요소 스트림 반환, 없으면 빈 스트림 반환 |
// 기존 방식
if (user != null && user.getName() != null) {
System.out.println(user.getName());
}
// Optional 사용
Optional.ofNullable(user)
.map(User::getName)
.ifPresent(System.out::println);
public Optional<User> findUser(String id) {
return Optional.ofNullable(userRepository.get(id));
}
필드로 사용 금지: Optional은 주로 리턴 타입으로 사용하는 것이 권장된다. 필드나 파라미터에는 사용하지 않는 게 좋다.
성능 민감 구간에서는 자제: 불필요한 객체 생성이 있기 때문에 성능이 중요한 곳에서는 사용을 피하는 것이 좋다.
Optional.get()은 지양: 값이 없을 때 예외를 던지므로 get()보다 orElse, ifPresent, orElseThrow 등을 활용해야 한다.
Optional은 null 안전성과 가독성을 높여주는 강력한 도구다. 단, 무분별하게 사용하기보다는 반환 값 처리용으로만 국한해서 사용하는 것이 바람직하다.