Optional이란?

진기·2024년 2월 24일

1. Optional 개념

NPE (NullPointerException)

  • 개발을 할 때 가장 많이 발생하는 예외 중 하나가 바로 NPE(NullPointerException)이다.
  • NPE를 피하려면 null 여부를 검사해야 하는데, null 검사를 해야하는 변수가 많은 경우 코드가 복잡해지고 번거롭다. 그래서 null 대신 초기값을 사용하길 권장하기도 한다.
List<String> names = getNames();
names.sort(); // names가 null이라면 NPE가 발생함

List<String> names = getNames();
// NPE를 방지하기 위해 null 검사를 해야함
if(names != null){
    names.sort();
}

Optional

  • Java 8부터 도입된 Optional은 값이 없는 경우를 표현하기 위한 클래스이다.
  • Optional 클래스는 제네릭을 사용하여 어떤 타입의 객체도 감싸서 담을 수 있다.
  • Optional 객체는 값이 존재할 수도 있고, 없을 수도 있다.
  • 이는 NullPointerException 예외를 방지할 수 있고, 코드의 안정성을 높이며 가독성을 향상시킨다.
public final class Optional<T> {

  // If non-null, the value; if null, indicates no value is present
  private final T value;
   
  ...
}

Optional 객체 생성

  • of() : 값이 null이 아닌 경우에만 Optional 객체를 생성
  • ofNullable() : 값이 null인 경우에도 Optional 객체를 생성
  • empty() : 값을 갖지 않는 빈(empty) Optional 객체를 생성

Optional 객체 접근

Optional 객체에 접근하기 위해선 get() 메서드를 사용한다. 하지만 이 방법은 값이 없는 경우에 예외가 발생할 수 있으므로, isPresent() 메서드를 사용하여 값이 존재하는지 여부를 먼저 확인하는 것이 좋다.

또한, Optional 객체에 값이 있을 경우에는 orElse() 나 orElseGet() 메서드를 사용하여 대체 값을 제공할 수 있다.

다른 메서드들과의 연결

Optional 객체는 다른 메서드들과 연결하여 사용할 수 있다.

  • map() 메서드를 사용하여 Optional 객체의 값을 변환
  • filter() 메서드를 사용하여 Optional 객체의 값을 검사

예시

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        String str = "Hello, World!"; // null이 아닌 값을 가지는 문자열 변수
        Optional<String> optionalStr = Optional.of(str); // Optional 객체 생성, 값이 null이 아니므로 of() 메서드 사용
        System.out.println(optionalStr); // 출력: Optional[Hello, World!]

        String nullStr = null; // null 값을 가지는 문자열 변수
        Optional<String> optionalNullStr = Optional.ofNullable(nullStr); // Optional 객체 생성, 값이 null이므로 ofNullable() 메서드 사용
        System.out.println(optionalNullStr); // 출력: Optional.empty

        Optional<String> emptyOptional = Optional.empty(); // 값을 갖지 않는 빈 Optional 객체 생성
        System.out.println(emptyOptional); // 출력: Optional.empty
    }

}

📌올바른 Optional 사용법

1) Optional 변수에 Null을 할당하지 않기

  • Optional은 컨테이너/박싱 클래스일 뿐이며, Optional 변수에 null을 할당하는 것은 Optional 변수 자체가 null인지 또 검사해야 하는 문제를 야기한다. 그러므로 값이 없는 경우라면 Optional.empty()로 초기화하는 것이 좋다.

2) 값이 없을 때 Optional.orElseX()로 값을 반환하기

  • Optional의 장점 중 하나는 함수형 인터페이스를 통해 가독성좋고 유연한 코드를 작성할 수 있다는 것이다. 가급적이면 isPresent()로 검사하고 get()으로 값을 꺼내기 보다는 orElseGet 등을 활용해 처리하는 것이 좋다.
  • orElseGet은 값이 준비되어 있지 않은 경우, orElse는 값이 준비되어 있는 경우에 사용하면 된다. 만약 null을 반환해야 하는 경우라면 orElse(null)을 활용하는 것이 좋다.
  • 만약 값이 없어서 throw해야하는 경우라면 orElseThrow를 사용하면 되고 그 외에도 다양한 메소드들이 있으니 적당히 활용하면 된다.

3) 단순히 값을 얻으려는 목적으로만 Optional을 사용하지 않기

  • 단순히 값을 얻으려고 Optional을 사용하는 것은 Optional을 남용하는 대표적인 경우이다. 이러한 경우에는 굳이 Optional을 사용해 비용을 낭비하는 것 보다는 직접 값을 다루는 것이 좋다.

4) 생성자, 수정자, 메소드 파라미터 등으로 Optional을 넘기지 않기

  • Optional을 파라미터로 넘기는 것은 상당히 의미없는 행동이다. 왜냐하면 넘겨온 파라미터를 위해 자체 null체크도 추가로 해주어야 하고, 코드도 복잡해지는 등 상당히 번거로워지기 때문이다.
  • Optional은 반환 타입으로 대체 동작을 사용하기 위해 고안된 것임을 명심해야 하며, 앞서 설명한대로 Serializable을 구현하지 않으므로 필드 값으로 사용하지 않아야 한다.

5) Collection의 경우 Optional이 아닌 빈 Collection을 사용하기

  • Collection의 경우 굳이 Optional로 감쌀 필요가 없다. 오히려 빈 Collection을 사용하는 것이 깔끔하고, 처리가 가볍다.

6) 반환 타입으로만 사용하기

  • Optional은 반환 타입으로써 에러가 발생할 수 있는 경우에 결과 없음을 명확히 드러내기 위해 만들어졌으며, Stream API와 결합되어 유연한 체이닝 api를 만들기 위해 탄생한 것이다.
profile
개발 성장 이야기

0개의 댓글