Optional을 이해하기 전에 Wrapper 클래스가 무엇인지 알아야 한다.
기본 타입을 객체로 다루기 위해서 사용하는 클래스
기본 타입 (Primitive type) |
래퍼 클래스 (Wrapper class) |
---|---|
char | Character |
int | Integer |
float | Float |
double | Double |
boolean | Boolean |
long | Long |
short | Short |
예시
Integer num = 11; // 자동 박싱
int n = num; // 자동 언박싱
// 문자열(래퍼 클래스)을 정수형(기본 타입)으로 변환하고 싶을 때
String example = "123";
int ex = Integer.parseInt(example);
System.out.println(ex); // 123
Java 8에 추가된 기능 중 하나로, NPE
를 방지하기 위해 사용하는 래퍼(Wrapper) 클래스이다.
NPE (Null Pointer Exeption): 실제 값이 아닌 null값을 가지고 있는 객체/변수를 호출할 때 발생하는 예외
null값에 대한 처리를 제대로 하지 않으면 NPE이 발생하고, 코드 중간중간 null값을 체크하는 것도 번거롭다.
➡ 제네릭을 사용해서 null값일 수도 있는 변수를 wrapper클래스로 감싸주자.
public final class Optional<T> {
// If non-null, the value; if null, indicates no value is present
private final T value;
...
}
>
사용법Optional 객체를 생성하려면 empty(), of(), ofNullable()을 사용해야 한다.
Optional<String> optVal = Optional.of(null); //NPE 발생
Optional<String> optVal = Optional.ofNullable(null); // OK
Optional<String> optVal = null ;
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화
Optional로 감쌀 기본 타입이 int, long, double인 경우 Optional<Integer>, Optional<Long>, Optional<Double> 대신 OptionalInt, OptionalLong, OptionalDouble을 사용하면 불필요한 박싱/언박싱이 일어나지 않는다.
Optional<String> opt = Optional.ofNullable("하이");
if(opt.isPresent()) {
System.out.println(opt.get());
}
// 메소드 참조 활용 ➡
opt.isPresent(System.out.println);
null값일 경우 orElse(), orElseGet(), orElseThrow() 등의 메소드를 이용하면 null값을 대체할 값을 지정할 수 있다. (isPresent() - get() 보다 유용하다)
세 메소드는 값이 존재하면 그 값을 반환하고 값이 존재하지 않으면 반환할 값에 따라 나뉜다.
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외 발생
String str2 = optVal.orElse(""); // optVal에 저장된 값이 null일 때는 ""를 반환
String str3 = optVal.orElseGet(() -> "def"); //optVal에 저장된 값이 null일 때 "def"를 반환하는 Supplier 객체를 반환
String str4 = optVal.orElseThrow(orElseThrow(()-> new Exception("값이 없습니다")) // optVal에 저장된 값이 null일 때는 예외가 메시지와 함께 발생
(인텔리제이)control + B로 JpaRepository를 찾아가보면 CrudRepository를 extend하고 있는 것을 확인할 수 있다.
CrudRepository에 정의된 findById() 메소드는 반환값이 Optional이다!
반환값이 Optional이기 때문에 null인 경우 Optional의 메소드를 사용해 객체가 null 처리를 쉽게 할 수 있다.
Optional을 반환받기 때문에 별다른 처리가 필요없어도 보통 orElse(), orElseGet() 또는 orElseThrow() 로 값을 받아온다.
ArticleService.java 일부
@Transactional(readOnly = true)
public ArticleWithCommentsDto getArticleWithComments(Long articleId) {
return articleRepository.findById(articleId)
.map(ArticleWithCommentsDto::from)
.orElseThrow(()-> new EntityNotFoundException("게시글이 없습니다 - articleId: " + articleId));
}