본 게시글은 Java API Document - Java Class Optional<T> (JDK 17) 와 자바의 정석의 내용을 옮겨 작성되었습니다.
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("이름 못 찾음")
);
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")
처럼 옵셔널 처리가 가능하다.
// 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()
메소드로 처리해주자.