Skip to content

Optional

자바에서 원시 타입을 제외한 모든 참조 변수는 null을 가질 수 있어, 이로 인해 NullPointerException(NPE)이 빈번하게 발생한다.

public static void main(String[] args) {
String str = "abc";
if (str != null) {
System.out.println(str.toUpperCase()); // ABC
} else {
System.out.println("null");
}
}

자바 8에서는 Optional 클래스를 도입하여 메서드 시그니처만 보고도 null 반환 가능성을 예측할 수 있게 하며, null 처리를 강제하는 효과를 준다.

Optionalnull이 될 수 있는 객체를 감싸는 래퍼 클래스로, null이 될 수 있는 객체를 담고 있는 Optional 객체를 생성할 수 있다.

public static void main(String[] args) {
Map<String, String> map = Map.of("existKey", "existValue");
Optional<String> opt1 = Optional.of(map.get("existKey")); // `null`이 아님을 보장할 때 사용
Optional<String> opt2 = Optional.ofNullable(map.get("notExistKey")); // `null`일 수도 있을 때 사용
Optional<String> opt3 = Optional.empty(); // `null`임을 명시적으로 나타낼 때 사용
}
메서드입력 값 null입력 값 null 아님
Optional.of(T value)NullPointerException 발생Optional 객체 생성 (값을 담고 있음)
Optional.ofNullable(T value)Optional.empty() 반환Optional 객체 생성 (값을 담고 있음)
Optional.empty()-(null을 담고 있는 Optional 객체 생성-(null을 담고 있는 Optional 객체 생성

Optional 객체에 담긴 값을 가져오기 위해서는 기본적으로 get() 메서드를 사용해서 가져올 수 있다.

public static void main(String[] args) {
Optional<String> opt = Optional.of("abc");
String str = opt.get();
System.out.println(str); // abc
}

하지만 비어있는 Optional 객체에 get()을 호출하면 NoSuchElementException이 발생할 수 있기 때문에, ifPresent()와 결합하여 사용해볼 수 있다.

public static void main(String[] args) {
Optional<String> opt = Optional.empty();
if (opt.isPresent()) {
String str = opt.get();
System.out.println(str);
}
}

하지만 위와 같은 방식은 if (value != null) { ... } 코드와 본질적으로 다르지 않으며, Optional의 장점을 활용하지 못한다.

올바른 Optional 값 조회 및 처리 방법

Section titled “올바른 Optional 값 조회 및 처리 방법”

Optional 객체의 값을 조회하고 처리하는 위해 다양한 메서드가 제공된다.

메서드값이 있을 때값이 없을 때
ifPresent(Consumer<? super T> action)Consumer 실행아무 동작도 하지 않음
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)Consumer 실행Runnable 실행
orElse(T other)값을 반환전달 받은 인자를 반환
orElseGet(Supplier<? extends T> other)값을 반환Supplier를 실행한 결과를 반환
orElseThrow(Supplier<? extends X> exceptionSupplier)값을 반환Supplier가 제공하는 예외를 발생
public static void main(String[] args) {
// ifPresent
Optional<String> opt1 = Optional.of("abc");
opt1.ifPresent(s -> System.out.println(s.toUpperCase())); // ABC
// ifPresentOrElse
Optional<String> opt2 = Optional.empty();
opt2.ifPresentOrElse(s -> System.out.println(s.toUpperCase()),
() -> System.out.println("null")); // null
// orElse
String value1 = Optional.of("abc")
.map(String::toUpperCase)
.orElse("null"); // "ABC"
// orElseGet
String value2 = Optional.empty()
.map(String::toUpperCase)
.orElseGet(() -> "null"); // "null"
// orElseThrow
Optional<String> opt5 = Optional.empty();
String value = opt5.orElseThrow(IllegalArgumentException::new);
}

orElse()orElseGet()Optional이 비어있을 때 대체 값을 제공하는 동일한 기능을 가진 메서드지만, 기본값을 생성하는 시점에서 차이가 있다.

  • orElse(T other): Optional에 값이 있든 없든 other 객체가 항상 생성
  • orElseGet(Supplier<? extends T> other): Optional에 값이 없을 때만 Supplier 람다식이 실행되어 기본값이 생성

때문에, 기본값 생성 비용이 큰 경우에는 orElseGet()을 사용하는 것이 성능상 유리하다.

Optional은 남용하면 오히려 코드를 복잡하게 만들 수 있다.

  • 메서드 반환 타입으로만 사용: Optional은 메서드의 반환 타입(return type)으로 사용되는 것을 권장
  • 필드(멤버 변수)로 사용 금지: 클래스의 필드 타입으로 Optional을 사용하는 것은 권장되지 않음
    • 직렬화 문제, JPA 등에서의 비호환성 문제
  • 메서드 매개변수로 사용 금지: 매개변수로 null을 보낼 수 있어 Optional 객체 자체가 null이 될 수 있음
    • 메서드 오버로딩으로 해결하는 것이 더 나음
  • 컬렉션 래핑 금지: 컬렉션은 null을 반환하기보다 비어있는 컬렉션(예: Collections.emptyList())을 반환하는 것을 권장

Last updated:

Java