Optional 이란?
개발을 진행하다 보면, Primitive Type(원시 타입)과 더불어 Reference Type(참조 타입)을 사용한다.
원시 타입과 달리 참조 타입은 Null 값을 발생시키고 이로 가장 많이 발생하는 예외 중 하나인 바로 NPE(NullPointerException)을 유발한다.
이를 피하려면, null 여부를 검사하고 처리하는 로직이 필요한데 이를 모든 코드에서 사용하는 것을 상상해보면,
꽤나 번거롭고 코드 가독성을 더럽힐 것이다.
따라서 Java는 8버전부터 Optional<T> 클래스를 사용해서 NPE를 방지할 수 있도록 도와준다.
이는 Null이 올 수 있는 값을 감쌀 수 있는 Wrapper 클래스이다.
이번 글에서는 Optional의 사용법에 대해 공부하고 정리해보겠다.
Optional<T> 생성 방법
Optional<T>을 생성하는 방법은 아래와 같다.
// 1. Optional.of()
Optional<String> opt = Optional.of("value");
Optional.of(null)
// -> NPE 발생 - Null 가능성이 있는 변수 넣지 않기
// 2. Optional.ofNullable() : Null이 가능한 값으로 생성
Optional<String> opt2 = Optional.ofNullable(someValue);
// 3. empty() : 빈 값
Optional<String> opt3 = Optinal.empty();
Optinal 객체의 값 구하기, 확인하기, 값의 여부에 따른 로직처리
get() 메소드 실행하기
Optional<String> opt = Optional.of("value");
String str = opt.get()
// str : "value"
// 단 값이 없는 Optional에 get()을 실행하면 예외가 터진다.
Optional<String> opt1 = Optional.empty();
opt1.get();
// NoSuchElementException !
Optional로 감싼 변수에 get()을 실행하면 변수에 담긴 값을 반환한다.
값이 있는지 확인하기 ( isPresent() / isEmpty() )
// isPresent() : 값이 있는지 확인
// isEmpty() : 값이 없는지 확인(자바 11)
Optional<String> opt = Optional.of("value");
opt.isPresent();
// true
opt.isEmpty();
// false
값이 존재하면 로직 수행하기 ( ifPresent() / ifPresentOrElse() )
// ifPresent()
// ifPresentOrElse()
Optional<String> opt = getValue();
opt.ifPresent(value -> doSome(value));
// if(value != null) { doSome(value) } 와 같다.
Optional<String> opt = getValue();
opt.ifPresentOrElse(
value -> doSome(value),
() -> doOther()
)
// if(value != null) { doSome(value); } else { doOther(); } 와 같다.
값이 없을 경우 다른 값 사용하기 ( orElse() / orElseGet() / or )
// orElse()
// orElseGet()
// or
Optional<String> opt1 = getValue();
String result = opt1.orElse("defalut");
Optional<String> opt2 = getValue();
String result = opt2.orElseGet(() -> "defalut");
Optional<String> opt3 = getValue();
String result = opt3.or(() -> Optional.of("defalut"));
// String value = getValue();
// String result = value == null ? "defalut" : value; 와 같다.
값이 없으면 예외, 있으면 리턴 ( orElseThrow() )
Optional<Member> opt = repository.findById("id");
Member m = opt.orElseThrow(() -> new NoMemberException());
m.block();
값 변환 및 조건에 따른 처리
map 사용
map에 전달 받은 함수를 실행하고 값을 변환하여 리턴하는 방법
만약 Optional로 감싼 변수(객체)가 값이 없다면 빈 Optional을 리턴한다.
Optional<Member> member = ...;
Optional<LocalDate> birthOpt = member.map(mem -> mem.getBirthday());
Optional<Integer> pdOpt = birthOpt.map(birth -> cal(birth));
Optional<Integer> pdOpt2 = member.map(mem -> mem.getBirthday())
.map(birth -> cal(birth));
// 빈 Optional은 map으로 전달한 함수를 실행하지 않고 빈 Optional을 리턴한다.
Optional<String> empty = Optional.empty();
Optional<String> empty2 = empty.map(str -> str.toUpperCase());
// empth2.isEmpty() -> true
flatMap 사용
Optional로 감싼 변수를 평면화하고 함수 이용 값을 Optional로 리턴하고 싶다면 사용하면 된다.
말로는 어렵다. 코드를 보자
Optional<Member> member = ...;
Optional<LocalDate> birthOpt = member.flatMap(mem -> Optional.ofNullable(mem.getBirthday()));
// Optional<LocalDate> birthOpt가 리턴된다.
// 만약 map을 사용한다면?
// Optional<Optional<LocalDate>> bitrhOpt 와 같은 형태가 된다.
filter 사용
조건을 충족하면 값 그대로 리턴하고 그렇지 않으면 빈 Optional을 리턴한다.
// 예를 들어 아래와 같은 코드가 있다.
String str = ...;
if(str != null && str.length() > 3) {
System.out.println(str);
}
// 이를 Optional과 filter를 사용하면 더욱 간결하게 처리 가능하다.
Optional<String> strOpt = ...;
Optional<String> filtered = strOpt.filter(str -> str.length()>3);
filtered.ifPresent(str -> System.out.println(str));
Optional<String> strOpt = ...;
Optional<String> filtered = strOpt.filter(str -> str.length()>3).ifPresent(str -> System.out.println(str));
두 개의 Optional 조합
두 개의 Optional 조합의 경우 (1)
// 예를 들어 아래와 같은 코드가 있다.
Member m = ...;
if(m == null) return null;
Company c = getCompony(m);
if(c == null) return null;
Card card = createCard(m, c);
return card;
// 이를 두 개의 Optional로 감싸 사용하는 경우 아래와 같이 작성될 수 있다.
// <방법1>
Optional<Member> member = ...;
Optional<Company> company = member.map(mem -> getCompany(mem));
Optional<Card> card = member.flatMap(mem -> company.map(comp -> createCard(mem, comp)));
// member의 Optional을 벗기고 해당 변수를 Optinal<company> 변수로 리턴하기 위해 map을 사용해서
// createCard에 mem과 comp 변수를 넣어 호출한다.
// <방법2>
Optional<Member> member = ...;
Optional<Card> card = member.faltMap(mem -> { Optional<Company> comp = getCompanyOptional(mem);
return comp.map(com -> createCard(mem, com));
});
두 개의 Optional 조합의 경우 (2)
// 예를 들어 아래와 같은 코드가 있다.
Member m1 = ...;
Member m2 = ...;
if(m1 == null && m2 ==null)
return null;
if(m1 ==null) return m2;
if(m2 ==null) reutrn m1;
return m1.year > m2.year ? m1 : m2
// 이를 두개의 Optional로 감싸 사용하는 경우 아래와 같이 작성될 수 있다.
Optional<Member> member1 = ...;
Optional<Member> member2 = ...;
Optional<Member> result =
member1.flatMap( m1 -> { // m1이 있으면
return member2.map(m2 -> { // m2가 있으면
return m1.year > m2.year ? m1 : m2}).orElse(m1) // m2가 없으면
}).or(() -> m2); //flatMap 결과(m1)이 없으면 m2 사용
return result.orElse(null);
참고한 자료
https://www.youtube.com/watch?v=RsUTolCVm_E&t=449s
'Java > 자바개념' 카테고리의 다른 글
상속 (0) | 2024.03.14 |
---|---|
final과 final class (1) | 2024.03.08 |
배열과 제어문 (1) | 2024.03.08 |
자바의 메모리 구조 (0) | 2024.02.27 |
댓글