[Java] Optional 의 문법과 활용

이상현·2024년 6월 23일
0

Java

목록 보기
20/21
post-thumbnail

본 게시글은 Java API Document - Java Class Optional<T> (JDK 17)자바의 정석의 내용을 옮겨 작성되었습니다.

Optional<T> 클래스

Optional은 null 일 수도 있고 아닐수도 있는 값을 보관하는 컨테이너이다.

보통 연산의 결과가 null 일수도 있을 때 null 인지 매번 if 문으로 체크하는 대신 Optional 에 정의된 메소드를 통해서 간단히 처리하기 위해 사용한다. 즉, NullPointerException 으로부터 안전해진다.

제네릭 클래스이므로 모든 타입의 참조변수를 담을 수 있다.

주요 메소드

  • .of(T value): Optional 객체를 생성한다.

    Optional<String> optVal = Optional.of("abc");
  • .ofNullable(T value): T 타입의 참조변수가 null 일 가능성이 있을때 객체를 생성하는 방식이다.

    Optional<String> optVal1 = Optional.of(null); // NullPointerException 발생
    Optional<String> optVal2 = Optional.ofNullable(null);
  • .empty(): Optional 을 빈 객체로 초기화 한다.

    Optional<String> optVal = Optional.empty()
  • .isPresent(): 값이 존재하는지 Bool 값으로 확인할 수 있다.

  • .get(): 객체에 저장된 값을 가져온다. null 이였을 경우 예외 발생

  • .orElse(T other): 객체에 저장된 값을 가져온다. 저장된 값이 null 일 경우 반환할 값을 지정할 수 있다.

    Optional<String> optVal = Optional.of("abc");
    String str = optVal.orElse("");
  • .orElseThrow(() -> new 예외): 값이 없을 때 예외를 던지도록 설정

    Long expiredTime = Optional.ofNullable(jwtProperties.getAccess_token_expiration())
           .orElseThrow(() -> new CustomException(AuthErrorCode.INVALID_TOKEN_TYPE));
  • .ifPresentOrElse(존재할 때 실행할 함수, 존재하지 않을 때 실행할 함수): 값이 있으면 첫 번째 매개변수에 전달된 액션을 수행하고, 그렇지 않으면 두번째 작업을 수행한다.

    findedFirstUser.ifPresentOrElse(
        name -> System.out.println("이름 찾음: " + name),
        () -> System.out.println("이름 못 찾음")
    );

활용 예시

null 이 발생 가능한 로직에서 사용

public class DateParser {

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static Optional<LocalDate> parseDate(String dateString) {
        try {
            return Optional.of(LocalDate.parse(dateString, DATE_FORMATTER));
        } catch (DateTimeParseException e) {
            return Optional.empty();
        }
    }
}

위 코드는 데이터베이스에서 가져온 날짜 문자열을 자바의 LocalDate 로 변환하는 코드이다.
LocalDate.parse() 는 실패할 가능성이 존재하는 코드이므로 실패할 경우 빈 옵셔널을 반환하도록 로직을 작성했다.

이렇게 하면 해당 메소드의 반환값을 사용하는 부분에서 null을 직접적으로 사용하지 않고 .orElse("N/A") 처럼 옵셔널 처리가 가능하다.

Stream 에서 find 관련 메소드의 반환값

// UserRepository.java
public Optional<User> findUserBySearchTerm(String searchTerm) {
    return findAllUsers().stream()
        .filter(user -> user.getName().contains(searchTerm))
        .findFirst();
}

// UserController.java
public void run() {
    UserRepository userRepository = new UserRepository();

    // 이름으로 유저 검색
    Optional<User> userOptional = userRepository.findUserBySearchTerm("이상현");

    // Optional - ifPresentOrElse 를 사용하여 결과를 처리
    userOptional.ifPresentOrElse(
        user -> System.out.println("유저 찾음: " + user.getName() + ", 생일: " + user.getBirthdate()),
        () -> System.out.println("유저 못 찾음")
    );
    
    // 또는 map 과 orElse 로 처리 (위 로직과 동일한 결과물)
    String userMessage = userOptional
        .map(user -> "유저 찾음: " + user.getName() + ", 생일: " + user.getBirthdate())
        .orElse("유저 못 찾음");

    System.out.println(userMessage);
}

스트림에서 .findFirst() 또는 .findAny() 와 같은 메소드를 많이 쓰는데, 이 둘의 반환값은 Optional 이다. 그 반환값을 .orElse() 메소드로 처리해주자.

0개의 댓글