Optional 클래스

홍윤's avatar
Sep 03, 2024
Optional 클래스
 

1. Optional이란

💡
Optional은 Java 8에서 도입된 클래스입니다. Optional 클래스는 값을 포함할 수 있거나 포함하지 않을 수 있는 컨테이너 객체입니다. 이 클래스의 주요 목적은 null로 인해 발생하는 오류(예: NullPointerException)를 방지하고, null을 직접 사용하는 대신 보다 명확하고 안전한 방법으로 값의 존재 여부를 처리하는 것입니다.

Optional의 기본 개념

  • 값이 있을 수도 있고 없을 수도 있다: Optional은 값이 있을 때 그 값을 포함하고, 값이 없을 때는 비어 있는 컨테이너입니다. 이를 통해 null 사용을 피하고, null 검사 없이 값의 유무를 명확히 처리할 수 있습니다.
  • 의미 있는 반환값: 메서드가 실제 값 대신 Optional을 반환하면, 그 메서드의 호출자는 반환된 값이 없을 경우를 명확히 처리해야 합니다. 이는 코드의 가독성을 높이고 오류를 줄이는 데 도움이 됩니다.

Optional 생성하기

Optional 객체를 생성하는 방법은 여러 가지가 있습니다:
  1. Optional.empty(): 비어 있는 Optional을 생성합니다.
    1. Optional<String> empty = Optional.empty();
  1. Optional.of(value): null이 아닌 값을 포함하는 Optional을 생성합니다. 값이 null이면 NullPointerException이 발생합니다.
    1. Optional<String> opt = Optional.of("Hello");
  1. Optional.ofNullable(value): 값이 null일 수 있는 경우 사용합니다. 값이 null이면 비어 있는 Optional을 반환하고, 그렇지 않으면 값을 포함하는 Optional을 반환합니다.
    1. Optional<String> opt = Optional.ofNullable(null); // Returns Optional.empty()

Optional 사용하기

Optional 클래스에는 값을 처리하고 검사하는 다양한 메서드가 있습니다:
  1. isPresent(): Optional이 값을 포함하고 있는지 여부를 반환합니다.
    1. Optional<String> opt = Optional.of("Hello"); if (opt.isPresent()) { System.out.println(opt.get()); // "Hello" }
  1. ifPresent(Consumer<? super T> action): Optional에 값이 있는 경우 지정된 동작을 실행합니다.
    1. Optional<String> opt = Optional.of("Hello"); opt.ifPresent(value -> System.out.println(value)); // "Hello"
  1. get(): Optional에 포함된 값을 반환합니다. 값이 없으면 NoSuchElementException이 발생합니다.
    1. Optional<String> opt = Optional.of("Hello"); String value = opt.get(); // "Hello"
  1. orElse(T other): Optional이 비어 있는 경우 대체 값을 반환합니다.
    1. Optional<String> opt = Optional.empty(); String value = opt.orElse("Default Value"); // "Default Value"
  1. orElseGet(Supplier<? extends T> other): Optional이 비어 있는 경우, 대체 값을 제공하는 함수를 실행하고 그 결과를 반환합니다.
    1. Optional<String> opt = Optional.empty(); String value = opt.orElseGet(() -> "Generated Value"); // "Generated Value"
  1. orElseThrow(Supplier<? extends X> exceptionSupplier): Optional이 비어 있는 경우, 예외를 던집니다.
    1. Optional<String> opt = Optional.empty(); String value = opt.orElseThrow(() -> new RuntimeException("Value is missing")); // Throws RuntimeException
  1. map(Function<? super T, ? extends U> mapper): Optional에 값이 있으면 값을 매핑하고, 새 값을 가진 Optional을 반환합니다.
    1. Optional<String> opt = Optional.of("Hello"); Optional<Integer> lengthOpt = opt.map(String::length); // Optional containing 5
  1. flatMap(Function<? super T, Optional<U>> mapper): Optional에 값을 매핑하지만, 매핑 결과가 이미 Optional일 때 사용합니다.
    1. Optional<String> opt = Optional.of("Hello"); Optional<Integer> lengthOpt = opt.flatMap(value -> Optional.of(value.length())); // Optional containing 5
  1. filter(Predicate<? super T> predicate): Optional의 값이 조건을 만족하는 경우, 그 값을 가진 Optional을 반환하고, 그렇지 않으면 Optional.empty()를 반환합니다.
    1. Optional<String> opt = Optional.of("Hello"); Optional<String> filteredOpt = opt.filter(value -> value.startsWith("H")); // Optional containing "Hello"

Optional의 장점

  • null 안전성: null 사용을 줄이고, NullPointerException 발생 가능성을 줄입니다.
  • 의미 있는 API 설계: 메서드의 반환 타입에 Optional을 사용함으로써, 호출자에게 값의 유무에 대한 처리를 강제할 수 있습니다.
  • 명확한 코드: Optional 사용을 통해 코드의 의도를 명확히 할 수 있으며, 불필요한 null 검사를 줄입니다.

Optional의 단점

  • 불필요한 객체 생성: Optional은 객체이므로, 매우 많은 호출에서 사용하면 성능에 영향을 미칠 수 있습니다.
  • null 대체는 아님: Optional을 사용하는 것이 무조건적인 null 대체는 아닙니다. 특히 필드나 컬렉션에 Optional을 사용하는 것은 권장되지 않습니다.
Optional은 값의 존재 여부를 안전하게 처리하고 명확한 코드를 작성하는 데 매우 유용한 도구입니다. 상황에 맞게 적절히 사용하여 코드의 가독성 및 안전성을 높일 수 있습니다.

 

Optional 예제 코드 설명

💡
이 코드는 Java에서 Optional을 사용하여 null 값을 안전하게 처리하는 방법을 보여주는 테스트 메서드입니다. Optional 클래스는 Java 8에서 도입된 기능으로, null로 인한 NullPointerException을 방지하고 더 안전한 코드를 작성하는 데 도움이 됩니다. 각 부분을 설명해 드리겠습니다.
@Test public void go() { User user = null; Optional<User> op = Optional.ofNullable(user);
  • User user = null;: user 변수를 null로 초기화합니다. 즉, 현재 user는 어떤 User 객체도 참조하지 않고 있습니다.
  • Optional<User> op = Optional.ofNullable(user);: user 변수를 Optional로 감쌉니다. Optional.ofNullable()usernull일 경우 Optional.empty를 반환하고, 그렇지 않으면 Optionaluser 객체를 담아 반환합니다.

주석 처리된 코드 설명

1. 기본적인 값 가져오기

// User userPS = op.get(); // System.out.println(userPS.getId());
  • op.get(): Optional 객체에서 값을 직접 꺼내는 메서드입니다. op가 비어 있으면 NoSuchElementException을 던집니다.
  • 이 코드에서는 usernull이기 때문에 op.get()을 호출하면 예외가 발생할 것입니다. 따라서 이 코드는 주석 처리되어 있습니다.

2. 값이 존재하는지 확인한 후 처리하기

// if(op.isPresent()){ // User userPS = op.get(); // }else{ // throw new RuntimeException("존재하지 않아요"); // }
  • op.isPresent(): Optional 객체에 값이 있는지 확인합니다. 값이 있으면 true, 없으면 false를 반환합니다.
  • 이 코드 블록은 op에 값이 있으면 (isPresent()true이면) 값을 꺼내와서 userPS에 저장하고, 그렇지 않으면 예외를 던집니다.
  • 현재 usernull이므로 op는 비어 있고, isPresent()false를 반환하여 RuntimeException이 발생합니다.

3. 값이 없을 경우 기본 값 제공

// User u = User.builder().id(1).username("ssar").build(); // User u2 = op.orElse(u); // System.out.println(u2.getUsername());
  • User u = User.builder().id(1).username("ssar").build();: 새로운 User 객체를 빌더 패턴으로 생성합니다. id가 1이고 username"ssar"인 객체를 만듭니다.
  • User u2 = op.orElse(u);: op가 비어 있으면 u를 반환하고, 그렇지 않으면 op에 담긴 객체를 반환합니다.
  • 이 경우, usernull이므로 op는 비어 있고, u2u의 값을 가지게 됩니다.
  • System.out.println(u2.getUsername());: u2username을 출력합니다. 결과적으로 "ssar"가 출력됩니다.

4. 값이 없을 경우 예외 던지기

User u = op.orElseThrow(() -> new RuntimeException("fdsafdsafdsa"));
  • op.orElseThrow(() -> new RuntimeException("fdsafdsafdsa"));: op가 비어 있으면 RuntimeException을 던집니다. 람다식을 사용하여 예외를 생성합니다.
  • 이 경우, usernull이므로 op는 비어 있고, 예외가 발생합니다. 예외 메시지는 "fdsafdsafdsa"입니다.

전체 코드 요약

이 테스트 메서드는 Optional을 사용하여 null 가능성을 처리하는 방법을 여러 가지 보여줍니다:
  1. 값을 직접 가져오려고 시도할 수 있지만, 값이 없을 경우 예외가 발생할 수 있습니다.
  1. 값이 있는지 먼저 확인한 후에 안전하게 처리할 수 있습니다.
  1. 기본 값을 제공하여, 값이 없을 경우 기본 값을 사용할 수 있습니다.
  1. 값이 없을 경우 사용자 정의 예외를 던질 수 있습니다.
각 접근 방식은 상황에 따라 유용하게 사용할 수 있으며, Optional을 활용하면 null 처리를 보다 명확하고 안전하게 할 수 있습니다.
Share article

Uni