Java

[Java] Optional 개념 알기

suoop 2024. 5. 30. 01:33

💡Optional 도입 배경

-> NPE(NullPointerException)을 피하기 위해서.

자바로 프로그래밍을 하다보면 정의되지 않은 객체에 대해 null 값을 고려해야 하는 경우가 발생한다.

안정적인 실행을 위해서는 NULL 여부 검사를 통해, NPE가 발생하지 않도록 해야 한다.

단순한 코드라면 아래 예제와 같이 짧은 로직으로 처리할 수 있지만, 고려해야할 변수가 많아지면 NULL 체크 로직이 길어지므로 코드가 복잡해지는 한계가 있다.

List<String> names = getNames();
names.sort(); // names가 null이라면 NPE가 발생함

List<String> names = getNames();
// NPE를 방지하기 위해 null 검사를 해야함
if(names != null){
    names.sort();
}
출처: https://mangkyu.tistory.com/70 [MangKyu's Diary:티스토리]

 

 

 

💁Optional 이란

위 상황을 해결하기 위해 등장한 것이 바로 Optional 이다.

 

Java8에서 Optional<T> 클래스가 도입되었는데,

Optional<T>는 Null 이 올 수 있는 값을 감싸는 Wrapper 클래스이다. 쉽게 말해 Null 을 담을 수 있다.

이를 통해 NPE를 방지할 수 있고, 코드의 안정성을 높이며 가독성을 향상시킨다.

 

아래 Optional<T> 클래스에서, 

'EMPTY'null 값을 가지는 빈 Optional 객체를 나타낸다.

'value'는 T타입으로, 실제 값이 있을 때만 해당 값을 담게 된다.

/**
     * Returns an {@code Optional} describing the given non-{@code null}
     * value.
     *
     * @param value the value to describe, which must be non-{@code null}
     * @param <T> the type of the value
     * @return an {@code Optional} with the value present
     * @throws NullPointerException if value is {@code null}
     */
    public static <T> Optional<T> of(T value) {
        return new Optional<>(Objects.requireNonNull(value));
    }

 

 

 

👻 Optional 객체 생성하기 - 3가지 메서드

1. Optional.empty(): 값이 null인 경우

어떤 데이터의 값이 없는 경우, Optional.empty()로 생성할 수 있다.

Optional<String> optional = Optional.empty();

System.out.println(optional); // Optional.empty
System.out.println(optional.isPresent()); // false

 

위에서 설명했듯이, Optional 클래스는 내부에서 static 변수로 EMPTY 객체를 미리 생성해서 가지고 있다.

따라서 빈 객체를 여러 번 생성해줘야 하는 경우에도 1개의 EMPTY 객체를 공유함으로써 메모리를 절약한다.

 

2. Optional.of(): 값이 null이 아닌 경우

만약 어떤 데이터가 '절대' null이 아니라면 Optional.of()로 생성할 수 있다. 

만약 Optional.of()로 Null을 저장하려고 하면 NPE가 발생한다.

 

⬇️ Optional<T> 클래스 내용

/**
     * Returns an {@code Optional} describing the given non-{@code null}
     * value.
     *
     * @param value the value to describe, which must be non-{@code null}
     * @param <T> the type of the value
     * @return an {@code Optional} with the value present
     * @throws NullPointerException if value is {@code null}
     */
    public static <T> Optional<T> of(T value) {
        return new Optional<>(Objects.requireNonNull(value));
    }

 

⬇️ 예시

// Optional의 value는 절대 null이 아니다.
Optional<String> optional = Optional.of("MyName");

 

 

3. Optional.ofNullable(): 값이 null일 수도, 아닐 수도 있는 경우

만약 어떤 데이터의 값이 null일 수도 있고 아닐 수도 있는 경우에는 Optional.ofNullable()로 생성할 수 있다.

null인 경우에는 빈 Optional 객체가 반환되고, null이 아닌 경우에는 value값을 가지는 Optional 객체가 반환된다.

그리고 orElse 또는 orElseGet 메서드를 이용하여 null인 경우라도 다른 값을 리턴할 수 있다.

 

⬇️ Optional 클래스 내용

 /**
     * Returns an {@code Optional} describing the given value, if
     * non-{@code null}, otherwise returns an empty {@code Optional}.
     *
     * @param value the possibly-{@code null} value to describe
     * @param <T> the type of the value
     * @return an {@code Optional} with a present value if the specified value
     *         is non-{@code null}, otherwise an empty {@code Optional}
     */
    @SuppressWarnings("unchecked")
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? (Optional<T>) EMPTY
                             : new Optional<>(value);
    }

 

⬇️ 예시

// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("anonymous"); // 값이 없다면 "anonymous" 를 리턴

 

 

☝️Optional을 사용해야 하는 경우

더보기

Optional은 null 또는 값을 감싸서 NPE로부터 부담을 줄이기 위해 등장한 Wrapper 클래스이다.

Optional은 값을 Wrapping하고 다시 풀고, null일 경우에는 대체하는 함수를 호출하는 등의 오버헤드가 있으므로 잘못 사용하면 시스템 성능이 저하된다. 또한 Optional은 반환 타입으로써 제한적으로 사용되도록 설계되었다.

그렇기 때문에 반환 값이 절대 null이 아니라면 Optional을 사용하지 않는 것이 좋다.

즉, Optional은 메서드의 결과가 null이 될 수 있으며, null에 의해 오류가 발생할 가능성이 매우 높을 때 반환값으로만 사용되어야 한다. 

'언제 Optional을 사용해야 하는지'에 대한 자세한 내용은 따로 포스팅할 예정이다.

 

*오버헤드: 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등을 말한다.


Reference