Optional ?
Java8에서는 Optional<T> 클래스를 이용하여 NullPointerException(NPE)를 방지할 수 있도록 보조해준다.
Optional<T>는 NULL이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조를 하더라도 NPE가 발생하지 않도로 도와주며, 클래스이기 때문에 각종 메소드를 제공해준다.
Optional 생성
Optional.empty() - 값이 NULL인 경우
Optional<String> optional = Optional.empty();
System.out.println(optional); // Optional.empty
Optional.of() - 값이 NULL이 아닌 경우
만약 Optional.of()로 NULL을 저장하려고 하면 NPE가 발생한다.
// Optional의 value는 절대 null이 아니다.
Optional<String> optional = Optional.of("MyName");
Optional.ofNullable - 값이 NULL일수도, 아닐수도 있는 경우
생성한 이후에 orElse 또는 orElseGet 메소드를 이용하여 값이 없는 경우라도 안전하게 가져올 수 있다.
// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("anonymous"); // 값이 없다면 "anonymous" 를 리턴
Optional 사용 예시 코드
1. Optional<T>와 Lambda를 이용하여 기존의 소스보다 간단하게 표현할 수 있다.
// Java8 이전
List<String> names = getNames();
List<String> tempNames = list != null
? list
: new ArrayList<>();
// Java8 이후
List<String> nameList = Optional.ofNullable(getNames())
.orElseGet(() -> new ArrayList<>());
2. NULL 값 체크를 하기 위한 로직을 간단하게 표현할 수 있다.
public String findPostCode() {
UserVO userVO = getUser();
if (userVO != null) {
Address address = user.getAddress();
if (address != null) {
String postCode = address.getPostCode();
if (postCode != null) {
return postCode;
}
}
}
return "우편번호 없음";
}
public String findPostCode() {
// 위의 코드를 Optional로 펼쳐놓으면 아래와 같다.
Optional<UserVO> userVO = Optional.ofNullable(getUser());
Optional<Address> address = userVO.map(UserVO::getAddress);
Optional<String> postCode = address.map(Address::getPostCode);
String result = postCode.orElse("우편번호 없음");
// 그리고 위의 코드를 다음과 같이 축약해서 쓸 수 있다.
String result = user.map(UserVO::getAddress)
.map(Address::getPostCode)
.orElse("우편번호 없음");
}
Optional의 orElse와 orElseGet
orElse와 orElseGet의 차이
Optional API의 단말 연산에는 orElse와 orElseGet함수가 있다. 비슷하지만 서로 다른 기능을 한다.
- orEsle : 파라미터로 값을 받는다.
- orElseGet : 파라미터로 함수형 인터페이스(함수)를 받는다.
사용 예시 코드
첫번째 함수는 값이 비어있을 때 orElse를 호출하고, 두번째 함수는 orElseGet을 호출한다.
public void findUserEmailOrElse() {
String userEmail = "Empty";
String result = Optional.ofNullable(userEmail)
.orElse(getUserEmail());
System.out.println(result);
}
public void findUserEmailOrElseGet() {
String userEmail = "Empty";
String result = Optional.ofNullable(userEmail)
.orElseGet(this::getUserEmail);
System.out.println(result);
}
private String getUserEmail() {
System.out.println("getUserEmail() Called");
return "win-bro.tistory.com";
}
실행결과
// 1. orElse인 경우
getUserEmail() Called
Empty
// 2. orElseGet인 경우
Empty
위와 같은 결과가 나오는 이유는
orElse인 경우는 처리 순서는 다음과 같다.
- Optional.ofNullable로 "Empty를 갖는 Optional 객체 생성
- getUserEmail()가 실행되어 반환값을 orElse 파라미터로 전달
- orElse가 호출됨. "Empty"가 NULL이 아니므로 "Empty"를 그대로 가짐
orElseGet인 경우는 처리순서는 다음과 같다.
- Optional.ofNullable로 "Empty"를 갖는 Optional 객체 생성
- getUserEmail()함수 자체를 orElseGet 파라미터로 전달
- orElseGet이 호출됨. "Empty"가 Null이 아니므로 "Empty"를 그대로 가지며, getUserEmail()이 호출되지 않음
orElseGet에서는 파라미터로 넘어간 값인 getUserEmail함수가 NULL이 아니므로 .get에 의해 함수가 호출되지 않는다.
만약 Optional의 값으로 NULL이 있다면, 다음과 같은 흐름에 의해 orElseGet의 파라미터로 넘어온 getUserEmail()이 실행될 것이다.
public void findUserEmailOrElseGet() {
String result = Optional.ofNullable(null)
.orElseGet(this::getUserEmail);
System.out.println(result);
}
private String getUserEmail() {
System.out.println("getUserEmail() Called");
return "win-bro.tistory.com";
}
- Optional.ofNullable로 NULL을 갖는 Optional 객체 생성
- getUserEmail()자체를 orElseGet파라미터로 전달
- orElseGet이 호출됨, 값이 NULL이므로 other.get()이 호출되어 getUserEmail()가 호출됨
orElse는 값을 생성하여 orElseGet보다 비용이 크므로 최대한 사용을 자제해야한다.
orElse와 orElseGet의 차이 및 사용법 정리
orElse
- 파라미터로 값을 필요로 한다.
- 값이 미리 존재하는 경우에 사용한다.
orElseGet
- 파라미터로 함수(함수형 인터페이스)를 필요로 한다.
- 값이 미리 존재하지 않는 거의 대부분의 경우에 orElseGet을 사용하면 된다.
참고 : https://mangkyu.tistory.com/70
'Backend > JAVA' 카테고리의 다른 글
[JAVA] 메소드 오버라이딩 / 오버로딩을 통한 상속 다형성에 대한 이해와 Self 참조 (0) | 2022.10.11 |
---|---|
[JAVA] Map & HashMap (0) | 2022.07.21 |
[JAVA] Stream (스트림) (0) | 2022.06.20 |
[JAVA] Default method (디폴트 메소드) (0) | 2022.05.09 |
[JAVA] Functional Interface (함수형 인터페이스) (0) | 2022.05.03 |