λͺ©μ°¨


null이 μ£ΌλŠ” 문제

μ•žμ„  κΈ€μ—μ„œλŠ” μžλ°” μ–Έμ–΄μ—μ„œ null의 νŠΉμ§•κ³Ό ν•¨κ»˜ 잘λͺ»λœ null 참쑰둜 인해 λ°œμƒν•˜λŠ” λ¬Έμ œλ“€μ— λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μ•˜λ‹€. λ‚΄μš©μ„ 정리해보면, λŸ°νƒ€μž„μ—μ„œ null을 잘λͺ» μ°Έμ‘°ν•˜λŠ” κ²½μš°μ—λŠ” NullPointerException이 λ°œμƒν•˜μ—¬ λ¬Έμ œκ°€ λ°œμƒν•  수 있고 이λ₯Ό ν”Όν•˜κ³ μž μƒκ²¨λ‚˜λŠ” 체크 둜직으둜 인해 μ½”λ“œκ°€ λ³΅μž‘ν•΄μ§€λŠ” λ¬Έμ œκ°€ μžˆλ‹€.

사싀 null 처리λ₯Ό κ°œμ„ ν•˜λ €λŠ” λ…Έλ ₯은 Optional 클래슀 이전에도 μžˆμ—ˆλ‹€. μžλ°”7 의 β€œProject Coinβ€μ—μ„œλŠ” μ•ˆμ „ 호좜(safe call)을 μœ„ν•œ μ—˜λΉ„μŠ€ μ—°μ‚°μž(elvis operator)κ°€ μ œμ•ˆλ˜μ—ˆμœΌλ‚˜ κ²°κ³Όμ μœΌλ‘œλŠ” μŠΉμΈλ˜μ§€ μ•Šμ•˜λ‹€.

μ—˜λΉ„μŠ€ μ—°μ‚°μžλŠ” 보톡 ?:을 λ§ν•œλ‹€. μ‹œκ³„λ°©ν–₯으둜 90도 νšŒμ „ν•˜λ©΄ μ—˜λΉ„μŠ€ ν”„λ ˆμŠ¬λ¦¬(Elvis Presley) 의 λͺ¨μŠ΅μ΄ μ—°μƒλœλ‹€.

μƒˆλ‘œμš΄ μ•ˆμ „μž₯치λ₯Ό μ μš©ν•˜λŠ” 이점이 λ„μž…ν•˜λŠ” λΉ„μš©λ³΄λ‹€ μ’‹μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ΄κ² μ§€λ§Œ, μ œμ•ˆμ΄ μŠΉμΈλ˜μ—ˆλ‹€λ©΄ μš°λ¦¬λŠ” μžλ°”μ—μ„œ null이 μ£ΌλŠ” λ¬Έμ œλ“€μ„ μ•„λž˜μ™€ 같은 μ½”λ“œλ‘œ νšŒν”Όν–ˆμ„μ§€λ„ λͺ¨λ₯Έλ‹€.

// ν˜Ήμ‹œλ‚˜ 이런 μ½”λ“œκ°€ κ°€λŠ₯ν•˜μ§€ μ•Šμ•˜μ„κΉŒ?
public String getPhoneManufacturerName(Person person) {
    // 체이닝 μ€‘μ—μ„œ null이 μžˆλ‹€λ©΄, "Samsung"을 λ°˜ν™˜ν•œλ‹€.
    return person?.getPhone()?.getManufacturer()?.getName() : "Samsung";
}

κ·Έλ ‡λ‹€λ©΄, 보닀 λ‚˜μ€ null 처리λ₯Ό μœ„ν•΄ νƒ„μƒν•œ Optional ν΄λž˜μŠ€λŠ” λ¬΄μ—‡μΌκΉŒ?


Optional 클래슀

java.util.Optional<T> ν΄λž˜μŠ€λŠ” ν•¨μˆ˜ν˜•(Functional) 언어인 슀칼라(Scala)와 ν•˜μŠ€μΌˆ(Haskell)의 영ν–₯을 λ°›μ•„μ„œ νƒ„μƒν–ˆλ‹€. μ•„λ§ˆ 슀칼라의 Optionalκ³Ό ν•˜μŠ€μΌˆμ˜ Maybe의 영ν–₯이 μ•„λ‹κΉŒ μ‹Άμ§€λ§Œ, μžλ°”μ˜ Optional의 λͺ©μ μ€ λ°˜ν™˜λ˜λŠ” 값이 β€˜μ—†μŒβ€™μ„ λ‚˜νƒ€λ‚΄λŠ” 것이닀. μ΄λŠ” ν΄λž˜μŠ€μ— 달린 주석을 ν†΅ν•΄μ„œλ„ μ•Œ 수 μžˆλŠ”λ°, API Note 뢀뢄에 μ•„λž˜μ™€ 같이 개발자의 μ˜λ„κ°€ λͺ…μ‹œλΌμžˆλ‹€.

Optional is primarily intended for use as a method return type where there is a clear need to represent β€œno result,” and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

μ˜΅μ…”λ„μ€ 주둜 β€œκ²°κ³Ό μ—†μŒβ€μ„ λ‚˜νƒ€λ‚Ό ν•„μš”κ°€ λΆ„λͺ…ν•˜κ³  null을 μ‚¬μš©ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•  κ°€λŠ₯성이 μžˆλŠ” λ©”μ„œλ“œ 리턴 νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•˜κΈ° μœ„ν•œ 것이닀. μ˜΅μ…”λ„ νƒ€μž…μ˜ λ³€μˆ˜λŠ” κ·Έ μžμ²΄κ°€ null이면 μ•ˆ 되며 항상 Optional μΈμŠ€ν„΄μŠ€λ₯Ό κ°€λ¦¬μΌœμ•Ό ν•œλ‹€.

Optional은 λΉ—λŒ€μ–΄ λ§ν•˜λ©΄ μœ λ¦¬κ°™μ΄ νŒŒμ†λ˜κΈ° μ‰¬μš΄ 것을 포μž₯ν•  λ•Œ κ°μ‹ΈλŠ” 에어캑(일λͺ… 뽁뽁이)κ³Ό κ°™λ‹€. μ‰½κ²Œ κΉ¨μ Έλ²„λ¦¬λŠ” 유리처럼, 잘λͺ» 닀루면 μ‹¬κ°ν•œ 였λ₯˜λ₯Ό μΌμœΌν‚¬ 수 μžˆλŠ” null을 μ•ˆμ „ν•˜κ²Œ λ‹€λ£° 수 있게 ν•΄μ€€λ‹€.

그럼 μ΄μ œλΆ€ν„° Optional을 μ‚¬μš©ν•˜λŠ” 방법에 λŒ€ν•΄μ„œ ν•˜λ‚˜μ”© μ•Œμ•„λ³΄μž.


Optional 객체 μ„ μ–Έ

Optional 클래슀의 λͺ…μ„Έλ₯Ό 보면 μ•Œ 수 μžˆλ“―μ΄, μ œλ„€λ¦­ νƒ€μž…μœΌλ‘œ μ œκ³΅λœλ‹€. λ”°λΌμ„œ μ„ μ–Έκ³Ό λ™μ‹œμ— νƒ€μž… λ§€κ°œλ³€μˆ˜λ₯Ό μ§€μ •ν•œλ‹€. 즉, Optional이 κ°μ‹ΈλŠ” 객체의 νƒ€μž…μ„ μ§€μ •ν•œλ‹€.

Optional<Person> person; // Person νƒ€μž…μ˜ `Optional` λ³€μˆ˜
Optional<Phone> phone; // phone νƒ€μž…μ˜ `Optional` λ³€μˆ˜


Optional 객체 생성

Optional 객체λ₯Ό λ§Œλ“€κΈ° μœ„ν•œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œ(static factory method)κ°€ μ œκ³΅λœλ‹€.

Optional.empty()

λΉ„μ–΄μžˆλŠ” Optional 객체λ₯Ό μƒμ„±ν•œλ‹€. λ‚΄λΆ€μ μœΌλ‘œλŠ” Optional ν΄λž˜μŠ€κ°€ κ°€μ§„ μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜κ²Œ λœλ‹€. β€œλΉ„μ–΄μžˆλ‹€β€ λΌλŠ” μ˜λ―Έμ—μ„œ nullκ³Ό λΉ„μŠ·ν•˜μ§€λ§Œ, Optional.empty()λŠ” μ°Έμ‘°ν•˜λ”λΌλ„ NullPointerExceptionκ³Ό 같은 μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ” 점이 λ‹€λ₯΄λ‹€.

// `Optional` 클래슀의 정적 λ©€λ²„λ‘œ μ„ μ–Έλ˜μ–΄ 있음
private static final Optional<?> EMPTY = new Optional<>();

public static<T> Optional<T> empty() {
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

Optional.of(T value)

null이 μ•„λ‹Œ 값을 κ°μ‹ΈλŠ” Optional 객체λ₯Ό μƒμ„±ν•œλ‹€. μ—¬κΈ°μ„œ 값이 null인 경우 NullPointerException이 λ°œμƒν•œλ‹€.

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

Optional.ofNullable(T value)

null 값을 μ €μž₯ν•  수 μžˆλŠ” Optional 객체λ₯Ό μƒμ„±ν•œλ‹€. 값이 null인 경우 빈 Optional 객체λ₯Ό λ°˜ν™˜ν•œλ‹€. λ”°λΌμ„œ 인자둜 λ„˜κ²¨μ§€λŠ” 값이 null일지도 λͺ¨λ₯΄λŠ” μƒν™©μ—μ„œ μ‚¬μš©ν•˜λ©΄ λœλ‹€.

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}


κ°μ²΄λŠ” μƒμ„±ν–ˆλŠ”λ°, μ–΄λ–»κ²Œ μ‚¬μš©ν•˜μ§€?

μ΄λ²ˆκΈ€μ—μ„œλŠ” Optional ν΄λž˜μŠ€μ— λŒ€ν•œ μ†Œκ°œμ™€ Optional 객체λ₯Ό μƒμ„±ν•˜λŠ” 방법에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μ•˜λ‹€. μ΄μ–΄μ§€λŠ” κΈ€μ—μ„œλŠ” Optional 객체의 값을 필터링 ν•˜κ±°λ‚˜ λ‹€λ₯Έ ν˜•νƒœλ‘œ λ³€ν™˜μ‹œν‚€λŠ” 방법에 λŒ€ν•΄μ„œ μ†Œκ°œν•œλ‹€.