JPA의 findById 메서드 리턴 타입인 Optional 클래스는 Java 8에 추가된 새로운 API로 null 처리를 잘 다룰 수 있게 도와준다.

잘못된 예
Optional<Person> findById(Long id) {
// find person from db
if (result == 0) {
return null;
}
}
옳은 예
Optional<Person> findById(Long id) {
// find person from db
if (result == 0) {
return Optional.empty();
}
}
Optional은 내부 값을 null로 초기화한 싱글턴 객체를 Optional.empty() 메서드를 통해 제공하고 있고, "결과 없음"을 표현해야하는 경우라면 null 대신 Optional.empty()를 반환하여 적절하게 처리할 수 있다.
빈 Optional 객체에 get() 메서드를 호출하는 경우 NoSuchElementException이 발생한다.
옳은 예
Person person = findById(4).orElseThrow(PersonNotFoundException::new);
String name = person.getName();
잘못된 예
Optional<Person> maybePerson = findById(4);
String name = maybePerson.get().getName();
Person person = findById(4).orElse(UNKNOWN_PERSON);
문자열처럼 동일한 객체 참조를 반환해도 괜찮은 경우에 적합
orElse는 기본값으로 반환할 값을 인자로 받는 것
orElseGet()은 값이 없는 경우 이를 대신해 반환할 값을 생성하는 람다를 인자로 받음
-> 값이 없는 경우 매번 새로운 객체를 반환하는 경우에는 orElseGet()를 사용할 수 있음
findById(4).orElseGet(() -> new Person("UNKNOWN"));
값이 없는 경우, 기본값을 반환하는 대신 예외를 던져야하는 경우 Optional.orElseThrow()를 사용
findById(4).orElseThrow(() -> new NoSuchElementException("Person not found"));
findById(4).ifPresent((user) -> System.out.println(user.getName()));
나쁜 예:
Optional<Person> maybePerson = findById(4);
if (maybePerson.isPresent()) {
Person person = maybePerson.get();
System.out.println(person.getName());
} else {
throw new NoPersonFoundException("No person found id maches: " + 4);
}
좋은 예:
Person person = findById(4)
.orElseThrow(() -> new NoPersonFoundException("No person found id maches: " + 4));
System.out.println(person.getName());
반환을 위해 설계된 타입으로 메서드의 인자로 사용하거나 클래스의 필드로 선언하지 않아야함
그냥 빈 컬렉션 또는 배열을 반환하자
Optional은 불변 객체로, 컬렉션을 사용하게 되면 메모리 문제를 일으키는 원인이 될 수 있다.
스트림처럼 함수형 스타일로 코드 작성이 가능해짐
Address getUserAddress(long id) {
findById(id)
.map(Person::getAddress)
.map(Address::of)
.orElseGet(Address::emptyAddress());
}
스트림과 동일하게 값을 필터링하는 역할을 해서, 참인 경우에는 Optional이 반환되고, 그렇지 않은 경우에는 비어 있는 Optional을 반환
boolean isValidName(String username) {
return Optional.ofNullable(username)
.filter(this::isIncludeSpace)
.filter(this::isOverLength)
.filter(this::isDuplicate)
.isPresent();
}
참고
https://www.latera.kr/blog/2019-07-02-effective-optional/
https://mangkyu.tistory.com/203