1) Optional을 쓰는 이유
- 어떤 객체를 조회하여 객체 내부의 값을 가져오려고 할때 해당 값이 존재하지 않는다면 NullPointerException이 발생하는 것을 자주 마주칠 것이다. 아래의 코드처럼 말이다.
예시코드(문제 제기)
public class OptionalExample{
public static void main(String[] args) {
Friend myFriend = findByName("Kim");
System.out.println(myFriend.getAge());
}
private static Friend findByName(String name){
Friend friend = friendRepository.findByName(name);
return friend;
}
}
- 위의 코드를 살펴보자.
- 만약 DB에 "Kim" 이라는 친구가 없다면 main()에서 NullPointerException이 발생하게 될 것이다.
- 그렇다면 다음과 같이 null 체크를 하는 로직을 이용하여 null이 발생했을 때를 대처하는 로직이 필요하다.
예시코드(개선방안)
public class OptionalExample{
public static void main(String[] args) {
Friend myFriend = findByName("Kim");
if(myFriend != null) {
System.out.println(myFriend.getAge());
}
else {
System.out.println(0);
}
}
private static Friend findByName(String name){
Friend friend = friendRepository.findByName(name);
return friend;
}
}
- 이와 같이 null 값이 나올 수 있는 상황을 로직으로 처리할 수 있다.
- 하지만 이와 유사한 경우가 발생할 때마다 매번 예시와 같은 로직을 작성해야 한다는 반복적인 문제점이 발생한다.
- 이처럼 객체의 값이 null인 경우를 대비하여, 더욱 간결하고 명시적으로 처리하기 위해 Optional을 사용한다.
- 다음 코드를 살펴보자.
2) Optional 소개
예시코드(Optional을 써보자)
public class OptionalExample{
public static void main(String[] args) {
Optional<Friend> optionalFriend = findByName("Kim");
if(optionalFriend.isPresent()) { // optional 내부에 값이 존재하는지 판단, Boolean 반환
System.out.println(optionalFriend.get().getAge());
// Optional.get() - Optional에 들어있는 값을 가져오는 메서드,
// null인 경우 NoSuchElementException이 발생한다.
}
else {
System.out.println(0);
}
}
private static Optional<Friend> findByName(String name) {
Friend friend = friendRepository.findByName(name);
return Optional.ofNullable(friend); // null 또는 어떤 값이 Optional에 들어간다.
// return Optional.of(friend); // 무조건 값이 들어있는 경우에 사용(null이 없음)
}
}
- 위의 코드에서 Optional을 이용해 반환 객체가 null인 경우를 대처하는 로직을 작성하였다.
- 하지만 크게 다를바가 없어 보인다. 사실, 위의 예시는 Optional의 기본적인 메서드를 소개하기 위함으로 작성하였다.
- 그렇다면 Optional을 Optional 답게 사용한다면 어떻게 작성할 수 있을까?
2) Optional을 Optional 답게
a) Optional.orElse()
public static void main(String[] args) {
Optional<Friend> optionalFriend = findByName("Kim");
optionalFriend.orElse(new Friend("NOBODY", 0));
}
- Optional.orElse()는 Optional 내부의 값이 null인 경우 지정한 객체를 반환한다.
- 비슷한 메서드로 Optional.orElseGet()이 있는데, 이는 매개변수로 Supplier 람다식을 받는다.
- Optional.orElseGet()은 값이 존재하면 람다로 작성한 값을 가져오고, 값이 없다면 람다로 작성한 대체 값을 반환한다.
b) Optional.orElseThrow()
public static void main(String[] args) {
Optional<Friend> optionalFriend = findByName("Kim");
optionalFriend.orElseThrow();
}
- Optional.orElseThrow()는 Optional 내부에 값이 없는 경우 NoSuchElementException을 발생시킨다.
- 이는 사실상 Optional.get()과 동일한 기능인데, 자바에서는 get() 보다는 orElseThrow()를 사용할 것을 권장한다.
c) Optional.map()
public static void main(String[] args) {
Optional<Friend> optionalFriend = findByName("Kim");
optionalFriend.map(Friend:::getAge);
}
- Optional.map()은 Optional 내부의 값을 다른 형태로 바꿔주는 기능이다.
- 위의 코드를 보면 Optional 내부의 값인 Friend 객체의 값을 Friend객체의 getAge() 값으로 바꿔주는 람다식이 적혀있다.
- 즉 위의 코드는 Friend 객체의 age 필드 값을 반환한다.
d) Optional의 메서드 체이닝
public static void main(String[] args) {
Optional<Friend> optionalFriend = findByName("Kim");
optionalFriend.map(Friend:::getAge).orElse(0);
}
- Optional은 메서드 체이닝을 지원한다. 그러므로 위의 코드와 같이 여러개의 Optional 기능을 연결하여 사용할 수 있다.
- 이 외에도 다양한 Optional API가 존재하니, Optional 클래스를 구경해보는 것을 추천한다.
3) Optional은 언제 어떻게 써야할까?
- Optional을 사용하는 시기와 방법은 위의 예시에서 보여주었던 것처럼 반환형으로써 사용하는 것이 가장 적절하다.
- Optional의 목적 자체가 어떤 값 또는 객체 자체가 null인 경우 발생할 수 있는 NullPointerException을 방지하기 위한 목적이기 때문이다.
'Back-end > Java 개념' 카테고리의 다른 글
AOP(Aspect Oriented Programming) 개념과 구현 (0) | 2023.07.24 |
---|---|
자바에서 람다를 사용하는 방법 (0) | 2023.06.26 |
Unreachable Statement 오류 (0) | 2022.05.10 |
Collections와 Collection에 대하여 (0) | 2022.05.09 |
Java의 기본 자료형(Primitive) (0) | 2022.01.28 |
댓글