고급 3편 자바 섹션11 Optional
1. Optional이 필요한 이유
- NullPointerException(NPE): null 참조 접근 시 런타임 예외 발생.
- 가독성 저하: if (obj != null) 반복.
- 의도가 불명확: 반환값이 null일지 아닐지 메서드만 보고 알 수 없음.
➡️ 이를 해결하기 위해 Java 8에서 Optional 클래스가 도입됨.
2. Optional 생성 방법
Optional.of("value") // null 아님 확신
Optional.ofNullable(value) // null 가능성 있음
Optional.empty() // 명시적 빈값
3. Optional 값 꺼내기 (get() 대신 아래 사용 권장)
opt.orElse("기본값"); // 값 없으면 기본값 반환
opt.orElseGet(() -> "비용 큰 기본값 생성"); // 지연 평가
opt.orElseThrow(() -> new RuntimeException()); // 값 없으면 예외
opt.ifPresent(v -> System.out.println(v)); // 값 있으면 실행
opt.ifPresentOrElse(
v -> System.out.println(v),
() -> System.out.println("없음")
);
4. Optional 변환 메서드
있을 때만 조작하거나, 없으면 다른 걸로 대체하거나, Stream처럼 다른 형태로 바꿔주는 메서드들
opt.map(String::length); // 값 있으면 변환
opt.flatMap(v -> Optional.of(v.toUpperCase())); // 중첩 Optional 제거
// 위 예시 ex_ [Optional["abc"]] -> [Optional[3]]
opt.filter(v -> v.startsWith("A")); // 조건 만족 여부
opt.stream().forEach(System.out::println); // Stream 변환
5. orElse() vs orElseGet() 차이
- orElse(): 항상 인자를 먼저 계산 (즉시 평가)
- orElseGet(): 값이 없을 때만 람다 실행 (지연 평가)
opt.orElse(createExpensiveObject()); // 무조건 계산
opt.orElseGet(() -> createExpensiveObject()); // 필요할 때만 계산
- 즉시 평가 (Eager Evaluation): 값을 바로 계산하고 넘김
→ orElse() - 지연 평가 (Lazy Evaluation): 필요할 때만 계산
→ orElseGet()
예를들어 아래와같이, 지연평가는 값이 있다면 람다함수가 실행되지않는다. 따라서 값 생성이 간단할때는 간편히 orElse()를 쓰고, 값 생성이 무겁거나 자주 필요없을 때 효율적인 OrElseGet()을 사용한다.
Optional<String> opt = Optional.of("Hi");
String result = opt.orElseGet(() -> createExpensiveData());
// opt에 값 있으면 createExpensiveData() 실행되지 않음!
public static String createExpensiveData() {
System.out.println("비싼 데이터 생성 중...");
return "DATA";
}
6. 옵셔널 - 베스트 프랙티스
1. Optional은 "반환값에만", 필드에는 사용 ❌
❌ 잘못된 사용
public class Product {
private Optional<String> name; // 비추천
}
- Optional<String>이지만, null이 들어가면 null, Optional.empty(), Optional.of()가 모두 가능해져 혼란.
- 필드에는 단순 String 쓰고, 꺼낼 때 Optional로 감싸자.
✅ 올바른 예시
public class Product {
private String name;
public Optional<String> getNameAsOptional() { // 반환시점에 optional로 감싸주기
return Optional.ofNullable(name);
}
}
2. Optional을 매개변수로 사용 ❌
❌ 잘못된 예
public void processOrder(Optional<Long> orderId) { ... }
- Optional.empty()를 넘기든, null 넘기든 큰차이없음.
✅ 추천 방식 1: 명시적 null 허용
public void processOrder(Long orderId) {
if (orderId == null) {
System.out.println("Order ID is empty!");
} else {
System.out.println("Order ID: " + orderId);
}
}
✅ 추천 방식 2: 메서드 오버로딩
public void processOrder(long orderId) { // 방어적 코드(여기서는 null 허용, 내부에서 처리)
System.out.println("Order ID: " + orderId);
}
public void processOrder() { // 이 메서드는 orderId가 없을 때 호출할 경우
System.out.println("Order ID is empty!");
}
📌 요점: Optional은 "반환값"에 쓰고, "파라미터"로 받지 마라.
3. Optional<List> ❌ → 그냥 빈 리스트 반환
❌ 나쁜 예
public Optional<List<String>> getUserRoles(String userId) {
if (!foundUser) return Optional.empty();
return Optional.of(List.of("ADMIN", "USER"));
}
- Optional이 비었는지, 내부 List가 비었는지 이중 체크 필요해짐.
✅ 좋은 예
public List<String> getUserRoles(String userId) {
if (!foundUser) return Collections.emptyList();
return List.of("ADMIN", "USER");
}
📌 요점: 컬렉션은 Optional 대신 비어있는 컬렉션 반환!
4. isPresent() + get() ❌ → 대체 메서드 사용
❌ 나쁜 예
Optional<String> name = Optional.ofNullable("Hyunjung");
if (name.isPresent()) { // 사실상 null 체크와 다름없음..
System.out.println(name.get());
}
✅ 좋은 예
name.ifPresent(System.out::println);
String value = name.orElse("Default");
int length = name.map(String::length).orElse(0);
요점: get() 사용은 최소화하고, orElse / ifPresent / map 등으로 대체하자.
✅ 5. orElse() vs orElseGet() 차이 분명히 이해하기
차이 설명
- orElse(): 항상 값 생성 (즉시 평가)
- orElseGet(): 필요할 때만 값 생성 (지연 평가)
예제
String result1 = optional.orElse(createHeavyObject()); // 무조건 실행
String result2 = optional.orElseGet(() -> createHeavyObject()); // 값 없을 때만 실행
요점: 대체값 계산이 비용 크면 orElseGet, 단순값이면 orElse.
✅ 6. Optional이 항상 정답은 아님
사용하지 않아도 되는 상황
- 항상 값이 있는 경우
- 값이 없으면 예외 던지는 게 더 적절할 때
- Optional이 오히려 코드만 장황해질 때
- 고성능 루프나 로우레벨 코드 (객체 생성 오버헤드)
예시
public String getConfig() {
return "AlwaysPresentValue"; // Optional 필요 없음
}
public Entity findEntity(Long id) {
Entity e = repo.find(id);
if (e == null) throw new IllegalStateException("Not found");
return e;
}
요점: Optional은 "값이 없을 수도 있음"을 전달하고 싶을 때만!
8. Optional 적용 기준 요약
- "값이 없을 수 있음"을 명시해야 할 때 → Optional
- "반드시 값이 있어야 함" → 그냥 타입 사용 또는 예외 처리
- "성능 민감, 반복문 내부 등" → Optional 피하기
'Java' 카테고리의 다른 글
[Java] 김영한의 실전 자바 - 고급 3편 섹션12 함수형 프로그래밍 (1) | 2025.04.28 |
---|---|
[Java] 김영한의 실전 자바 - 고급 3편 섹션12 병렬 스트림 (0) | 2025.04.28 |
[Java] 김영한의 실전 자바 - 고급 3편 섹션7 스트림API (1) | 2025.04.12 |
[Java] 김영한의 실전 자바 - 고급 3편 섹션6 메서드 참조 (0) | 2025.04.12 |
[Java] 김영한의 실전 자바 - 고급 3편 섹션4,5 람다활용,익명클래스와의 차이 (+정적 팩토리 메서드) (0) | 2025.04.07 |