1. Optional<T>
1️⃣ Optional<T> 이란?
1) 기본 개념
- 값이 있을 수도 있고 없을 수도 있는 상황을 명시적으로 표현하기 위한 컨테이너 타입
Optional<T>
- 내부에 단 하나의 값 또는 값 없음(empty) 상태를 가짐
null을 직접 담지 않음
- Java 8부터 도입
2) 핵심 목적
null로 인한 오류(특히NullPointerException)를 줄이고, API의 의도를 분명히 하는 것
2️⃣ Optional이 필요한 이유
1) 기존 방식의 문제
String name = user.getName();
if (name != null) {
System.out.println(name.toUpperCase());
}null체크를 깜빡하기 쉽다
- 메서드 시그니처만 봐서는
null이 올 수 있는지 알기 어렵다
NullPointerException의 주요 원인
2) Optional 사용 시
Optional<String> name = user.getName(); name.ifPresent(n -> System.out.println(n.toUpperCase()));
- “이 값은 없을 수도 있다”는 사실이 타입으로 드러남
- 호출자가 반드시 처리하도록 유도
3️⃣ Optional 생성 방법
1) 값이 확실히 있을 때
Optional<String> opt = Optional.of("hello");⚠️
null 전달 시 즉시 NullPointerException2) 값이 있을 수도, 없을 수도 있을 때
Optional<String> opt = Optional.ofNullable(value);
value == null→Optional.empty()
- 가장 흔히 사용됨
3) 빈 Optional
Optional<String> opt = Optional.empty();
4️⃣ 값 확인 및 추출
1) 존재 여부 확인
if (opt.isPresent()) {
System.out.println(opt.get());
}- 가능은 하지만 권장되지 않음
- 결국
null체크와 다를 바 없음
2) ifPresent (권장)
opt.ifPresent(value -> System.out.println(value));
3) 기본값 제공
String result = opt.orElse("default");⚠️
orElse는 값이 있어도 인자가 항상 평가됨String result = opt.orElseGet(() -> expensiveOperation());- 필요할 때만 실행 → 성능상 더 안전
4) 값이 없으면 예외
String result = opt.orElseThrow();String result = opt.orElseThrow(() -> new IllegalStateException("값 없음"));5️⃣ map / flatMap / filter (핵심)
1) map – 값 변환
Optional<String> opt = Optional.of("java");
Optional<Integer> length =
opt.map(String::length);- 값이 없으면 자동으로 empty 유지
2) flatMap – Optional 중첩 방지
Optional<Optional<String>> ❌ Optional<String> ✔
Optional<User> user; Optional<String> email = user.flatMap(User::getEmail);
getEmail()이Optional<String>을 반환할 때 사용
3) filter – 조건 만족 시만 유지
Optional<String> opt = Optional.of("java");
opt.filter(s -> s.length() > 3)
.ifPresent(System.out::println);6️⃣ Optional 사용 시 권장 규칙 (중요)
✅ 사용해야 할 곳
- 메서드 반환 타입
Optional<User> findUserById(long id)- 스트림 파이프라인
- 값이 없음을 정상적인 흐름으로 표현할 때
❌ 피해야 할 곳
① 필드
class User {
Optional<String> name; // ❌
}② 메서드 파라미터
void print(Optional<String> name); // ❌③ 컬렉션 대신
Optional<List<User>> // ❌→
List<User> (빈 리스트 사용)7️⃣ Optional vs null (요약 비교)
항목 | null | Optional |
의도 표현 | ❌ 불명확 | ✅ 명확 |
타입 안정성 | ❌ | ✅ |
NPE 위험 | 높음 | 낮음 |
함수형 처리 | 불가 | 가능 |
8️⃣ 흔한 안티패턴
❌ get() 남용
opt.get(); // 값 없으면 예외❌ isPresent + get
if (opt.isPresent()) {
return opt.get();
}❌ Optional을 Serializable DTO 필드로 사용
9️⃣ 실습
1) Optional.of()
package ex05;
import java.util.Optional;
public class OptEx01 {
public static void main(String[] args) {
// 1. Optional of Method
String name = "ssar";
Optional<String> opt = Optional.of(name); // 선물박스 안에 값이 들어감 (of에는 null 들어갈 수 없음)
System.out.println(opt.get()); // get() - 값이 무조건 있으니까 꺼내! 실제 코드에서는 사용 X
System.out.println(opt.get().length());
}
}
실제 코드에서는 사용할 일 없음. 무조건 값이 들어갈거면 그냥 String으로 받으면 됨
2-1) Optional.ofNullable() - isEmpty()
package ex05;
import java.util.Optional;
public class OptEx01 {
public static void main(String[] args) {
// 2. Optional nullable
String name2 = null;
Optional<String> opt2 = Optional.ofNullable(name2);
if (opt2.isPresent()) { // 값이 있으면
System.out.println(opt2.get());
}
if (opt2.isEmpty()) { // null이면
throw new RuntimeException("name2에 값이 없어요");
}
}
}
2-2) Optional.ofNullable() - isPresent()
package ex05;
import java.util.Optional;
public class OptEx01 {
public static void main(String[] args) {
// 2. Optional nullable
String name2 = "cos";
Optional<String> opt2 = Optional.ofNullable(name2);
if (opt2.isPresent()) { // 값이 있으면
System.out.println(opt2.get());
}
if (opt2.isEmpty()) { // null이면
throw new RuntimeException("name2에 값이 없어요");
}
}
}
3-1) orElseThrow() - ①홀수
package ex05;
import java.util.Optional;
public class OptEx01 {
static String download(int i){
if(i % 2 == 0){
return null;
}else{
return i+"";
}
}
public static void main(String[] args) {
// 3. orElse
String name3 = download(1); // 짝수 = null, 홀수 = 값
Optional<String> opt3 = Optional.ofNullable(name3);
// 3-1. orElseThrow
String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요"));
System.out.println("value1 : " + value1);
// 3-2. orElse
}
}

3-1) orElseThrow() - ②짝수
package ex05;
import java.util.Optional;
public class OptEx01 {
static String download(int i){
if(i % 2 == 0){
return null;
}else{
return i+"";
}
}
public static void main(String[] args) {
// 3. orElse
String name3 = download(2); // 짝수 = null, 홀수 = 값
Optional<String> opt3 = Optional.ofNullable(name3);
// 3-1. orElseThrow
String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요"));
System.out.println("value1 : " + value1);
// 3-2. orElse
}
}

3-2) orElse() - ①홀수
package ex05;
import java.util.Optional;
public class OptEx01 {
static String download(int i){
if(i % 2 == 0){
return null;
}else{
return i+"";
}
}
public static void main(String[] args) {
// 3. orElse
String name3 = download(1); // 짝수 = null, 홀수 = 값
Optional<String> opt3 = Optional.ofNullable(name3);
// 3-1. orElseThrow
String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요"));
// System.out.println("value1 : " + value1);
// 3-2. orElse
String value2 = opt3.orElse("0");
System.out.println("value2 : " + value2);
}
}

3-2) orElse() - ②짝수
package ex05;
import java.util.Optional;
public class OptEx01 {
static String download(int i){
if(i % 2 == 0){
return null;
}else{
return i+"";
}
}
public static void main(String[] args) {
// 3. orElse
String name3 = download(2); // 짝수 = null, 홀수 = 값
Optional<String> opt3 = Optional.ofNullable(name3);
// 3-2. orElse
String value2 = opt3.orElse("0");
System.out.println("value2 : " + value2);
// 3-1. orElseThrow
// String value1 = opt3.orElseThrow(() -> new RuntimeException("opt3에 값이 없어요"));
// System.out.println("value1 : " + value1);
}
}

Share article