Java/자바개념

Optional 기초 사용법

_Jin_ 2025. 1. 18.

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

댓글