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 처리를 강제하는 효과를 준다.
Optional 객체 생성
Section titled “Optional 객체 생성”Optional은 null이 될 수 있는 객체를 감싸는 래퍼 클래스로, 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 객체 조회
Section titled “Optional 객체 조회”get() 메서드
Section titled “get() 메서드”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() vs orElseGet()
Section titled “orElse() vs orElseGet()”orElse()와 orElseGet()은 Optional이 비어있을 때 대체 값을 제공하는 동일한 기능을 가진 메서드지만, 기본값을 생성하는 시점에서 차이가 있다.
orElse(T other):Optional에 값이 있든 없든other객체가 항상 생성orElseGet(Supplier<? extends T> other):Optional에 값이 없을 때만Supplier람다식이 실행되어 기본값이 생성
때문에, 기본값 생성 비용이 큰 경우에는 orElseGet()을 사용하는 것이 성능상 유리하다.
Optional 활용 시 주의점
Section titled “Optional 활용 시 주의점”Optional은 남용하면 오히려 코드를 복잡하게 만들 수 있다.
- 메서드 반환 타입으로만 사용:
Optional은 메서드의 반환 타입(return type)으로 사용되는 것을 권장 - 필드(멤버 변수)로 사용 금지: 클래스의 필드 타입으로
Optional을 사용하는 것은 권장되지 않음- 직렬화 문제, JPA 등에서의 비호환성 문제
- 메서드 매개변수로 사용 금지: 매개변수로
null을 보낼 수 있어Optional객체 자체가null이 될 수 있음- 메서드 오버로딩으로 해결하는 것이 더 나음
- 컬렉션 래핑 금지: 컬렉션은
null을 반환하기보다 비어있는 컬렉션(예:Collections.emptyList())을 반환하는 것을 권장