Just Do It

[JAVA] Optional

by 핫도구
반응형

Optional은 null 참조로 인한 NullPointerException을 방지하고 더 명시적이고 안전한 코드를 작성할 수 있도록 도와준다.

Optional 이란?
Optional<T>는 값이 있을 수도 있고 없을 수도 있는 컨테이너 객체이다. null을 직접 다루는 대신, 값의 존재 여부를 명시적으로 표현하는 래퍼클래스이다.
Optional이 필요한 이유

1. NullPointerException 발생 위험
2. null 체크 코드가 반복되어 가독성 저하
3. null이 정상적인 상태인지 오류상태인지 불분명
4. 메서드 시그니처만으로는 null 반환 가능성을 알 수 없음

즉, null을 직접 다루면서 발생할 수 있는 위험과 불명확성을 없애기 위해 필요하다.

Optional 생성 방법

Optional을 생성하는 방법은 주로 3가지가 존재한다.

// 1. Optional.of() - null이 아닌 값으로 생성
String name = "Java";
Optional<String> optName = Optional.of(name);
// Optional.of(null); // NullPointerException 발생!

// 2. Optional.ofNullable() - null일 수도 있는 값으로 생성
String nullableName = getName(); // null을 반환할 수 있는 메서드
Optional<String> optNullable = Optional.ofNullable(nullableName);

// 3. Optional.empty() - 빈 Optional 생성
Optional<String> empty = Optional.empty();

여기에서 Optional.of(val)는 null이 아닌 값을 감쌀 때 사용한다. 만약 val이 null이라면 NullPointerException이 발생한다.
그리고 Optional.ofNullable(val)의 경우 값이 null일 수도 있는 값을 안전하게 감싸기 위해 사용한다. 그리고 Optional.empty()의 경우는 명시적으로 비어 있음을 표현하기 위해 사용한다. 즉, Optional.ofNullable(val)은 null일 수도 있는 변수 혹은 반환값을 Optional로 감쌀 경우에 사용하고 Optional.empty()은 명시적으로 비어있는 결과를 생성하고 반환할 때 사용한다.

Optional 주요 메서드

값 존재 확인 메서드
대표적인 값 존재 메서드는 isPresent(), isEmpty(), ifPresent(), ifPresentOrElse()등이 존재한다. 이들은 각각 값이 존재하면 true를 반환, 값이 없으면 true를 반환, 값이 있을 경우에만 Consumer 실행, 값이 존재하면 첫 번째 동작, 없다면 두 번째 동작을 한다.

Optional<String> opt = Optional.of("Hello");
// 값이 존재하면 true 반환
if (opt.isPresent()) {
	System.out.println("값이 존재"); // 값이 존재
}
// 값이 없으면 true 반환
if (opt.isEmpty()) {
	System.out.println("값이 없습니다.");
}

// 값이 있을 경우에만  Consumer 실행
opt.ifPresent(s -> System.out.println("값1 : " + s)); // 값1 : Hello

// 값이 존재하면 첫번째 동작, 없다면 두 번째 동작
opt.ifPresentOrElse(
	s -> System.out.println("값2 : " + s), // 값2 : Hello
	() -> System.out.println("값이 없습니다.2")
);

대표적인 값 가져오기 메서드는 get(), orElse(), orElseGet(), orElseThrow()가 존재한다. 이중 get()메서드는 사용을 피해야하는데 이유로는 값의 존재를 보장하지 못하고, 런타임 예외의 위험이 존재하며 Optional의 설계 의도에 반하기 때문에 다른 메서드를 사용하는 것이 좋다. orElse()는 값이 없으면 ()의 값을 반환하며 orElseGet() 메서드는 값이 없으면 Supplier로 기본값을 생성하며 orElseThrow는 값이 존재하지않으면 예외를 발생시키는 것이다. 또한, orElse()는 항상 안에 있는 값이 실행되지만 orElseGet()은 값이 없을 경우에만 실행되기 때문에 비용이 큰 연산은 orElseGet()메서드를 사용하는 것이 효율적이다.

// 값을 가져오는 메소드지만 사용을 권장하지 않는다.
String value = opt.get();

// 값이 없으면 ()의 값을 반환한다.
String result1 = opt.orElse("default value1");

// 값이 없으면 Supplier로 기본값 생성
String result2 = opt.orElseGet(() -> "default value2");

// 값이 없으면 예외 발생
String result3 = opt.orElseThrow(() -> new IllegalArgumentException("값이 없습니다."));

대표적인 변환 메서드는 map(), flatMap(), filter()가 존재한다. 이들은 각각 값을 Optional로 반환하는데 값이 존재하면 [] 안에 값을 넣어서 반환한다. 만약 존재하지 않는다면 empty를 반환한다. 여기에서 map은 일반 값을 반환하지만 flapMap은 Optional을 반환하는 함수에 사용한다.

Optional<String> name = Optional.of("java");
// 값이 있으면 반환 함수 적용
Optional<String> upperName = name.map(String::toUpperCase);
System.out.println("upperName : " + upperName); // Optional[JAVA]

// Optional을 반환하는 함수
Optional<Integer> length = opt.flatMap(s -> Optional.of(s.length()));
System.out.println("length : " + length); // Optional[5]

// 조건에 맞는 경우만 유지
Optional<String> filtered = name.filter(s -> s.length() > 3);
System.out.println("filtered : " + filtered); // Optional[java]
filtered = name.filter(s -> s.length() > 5);
System.out.println("filtered : " + filtered); // Optional.empty
Optional의 성능

Optional은 객체 생성비용이 있기 때문에 성능이 매우 중요한 경우에 고려해야하는 요인이 된다.
그래서, 기본타입에는 OptionalInt, OptionalLong, OptionalDouble 사용을 권하고 핫스팟에서는 Optional보다 전통적인 null체크를 활용하는 것을 고려해야한다.

그러면 Optional은 Public API의 반환 타입으로 null 가능성을 명시하거나, 함수형 체이닝으로 깔끔한 코드를 작성하거나 값의 부재가 반드시 정상적인 비즈니스로직일 경우일 떄는 사용하는 것이 좋지만 이런 것들이 아닌, 내부 메서드 성능이 중요한 코드, DTO,Entity 필드에서는 전통적인 null 체크가 적합하다.


정리
Optional은 null 안정성을 높이고 의도를 명확히 표현하는 도구이다. 메서드 반환 타입으로 주요 사용하며, 함수형 프로그래밍 스타일과 잘 어울린다. 하지만, 모든 곳에서 사용하기보다 적절한 상황에서 올바르게 사용하는 것이 중요하다.

반응형

'JAVA > MID' 카테고리의 다른 글

[JAVA] Stream(1)  (0) 2025.09.24
[JAVA] 람다  (0) 2025.09.20
[JAVA] 제네릭(Generic)  (0) 2025.09.12
[JAVA] 예외처리(2)  (0) 2025.09.11
[JAVA] 날짜와 시간 조작하기  (0) 2025.09.05

블로그의 정보

AquaMan

핫도구

활동하기