Optional

Java 8 μ—μ„œ 처음 λ“±μž₯ν•œ κ°œλ…μœΌλ‘œ null 의 μ ‘κ·Ό 방식에 λŒ€ν•œ ν•΄κ²°μ±…μœΌλ‘œ λ“±μž₯ ν•˜μ˜€λ‹€.

NPE (Null Pointer Exception)

전톡적인 NPE λ°©μ–΄ νŒ¨ν„΄μ€ λ‹€μŒ λ‘κ°€μ§€λ‘œ λ‚˜λ‰œλ‹€.

쀑첩 null μ²΄ν¬ν•˜κΈ°

public String getCityOfMemberFromOrder(Order order) {
  if (order != null) {
    Member member = order.getMember();
      if (member != null) {
      Address address = member.getAddress();
        if (address != null) {
        String city = address.getCity();
        if (city != null) { return city; }
      }
    }
  }
return "Seoul"; // default
}

μ‚¬λ°©μ—μ„œ return ν•˜κΈ°

public String getCityOfMemberFromOrder(Order order) {
  if (order == null) {
    return "Seoul";
  }

  Member member = order.getMember();
  
  if (member == null) {
    return "Seoul";
  }

  Address address = member.getAddress();

  if (address == null) {
    return "Seoul";
  }

  String city = address.getCity();
  
  if (city == null) {
    return "Seoul";
  }
  
  return city;
}

Optional μ΄λž€?

Optional 은 μ‘΄μž¬ν•  μˆ˜λ„ μžˆμ§€λ§Œ μ•ˆ ν• μˆ˜λ„ μžˆλŠ” 객체, 즉 null 이 될 μˆ˜λ„ μžˆλŠ” 객체 λ₯Ό 감싸고 μžˆλŠ” μΌμ’…μ˜ Wrapper Class 이닀.

Optional μ‚¬μš©λ²•

Generic 을 μ œκ³΅ν•˜κΈ° λ•Œλ¬Έμ— λ³€μˆ˜ μ„ μ–Έν•  λ•Œ λͺ…μ‹œν•œ νƒ€μž…μ— λ”°λΌμ„œ κ°μŒ€μˆ˜ μžˆλŠ” 객체의 νƒ€μž…μ΄ κ²°μ •λœλ‹€.

μ„ μ–Έ

// Order νƒ€μž…μ˜ 객체λ₯Ό κ°μŒ€ 수 μžˆλŠ” Optional νƒ€μž… λ³€μˆ˜
Optional<Order> maybeOrder;

// Member νƒ€μž…μ˜ 객체λ₯Ό κ°μŒ€ 수 μžˆλŠ” Optional νƒ€μž… λ³€μˆ˜
Optional<Member> optMember;

// Address νƒ€μž…μ˜ 객체λ₯Ό κ°μŒ€ 수 μžˆλŠ” Optional νƒ€μž… λ³€μˆ˜
Optional<Address> address;

생성

Optional ν΄λž˜μŠ€λŠ” κ°„νŽΈν•œκ²Œ 객체λ₯Ό 생성할 수 μžˆλŠ” 3κ°€μ§€μ˜ 정적 νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

  • Optional.empty()

null 을 λ‹΄κ³ μžˆλŠ” λΉ„μ–΄μžˆλŠ” Optional 객체λ₯Ό μ–»μ–΄μ˜΅λ‹ˆλ‹€.
이 κ°μ²΄λŠ” Optional λ‚΄λΆ€μ μœΌλ‘œ 미리 생성해놓은 μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€μž…λ‹ˆλ‹€.

Optional<Member> maybeMember = Optional.empty();
  • Optional.of(value)

null 이 μ•„λ‹Œ 객체λ₯Ό λ‹΄κ³ μžˆλŠ” Optional 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
null 이 λ„˜μ–΄μ˜¬λ•Œ NPE λ₯Ό λ˜μ§€κΈ° λ•Œλ¬Έμ— μ£Όμ˜ν•΄μ„œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

Optional<Member> maybeMember = Optional.of(aMember);
  • Optional.ofNullable(value)

null 인지 μ•„λ‹Œμ§€ ν™•μ‹ ν•  수 μ—†λŠ” 객체λ₯Ό λ‹΄κ³ μžˆλŠ” Optional 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

Optional.empty() 와 Optional.ofNullable(value) λ₯Ό 합쳐놓은 λ©”μ†Œλ“œλΌκ³  μƒκ°ν•˜λ©΄ 쉽닀.

ν•΄λ‹Ή 객체가 null 인지 μ•„λ‹Œμ§€ 확신이 μ—†λŠ” μƒν™©μ—μ„œ ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

Optional<Member> maybeMember = Optional.ofNullable(aMember);
Optional<Member> maybeMember = Optional.ofNullable(null);

객체 μ ‘κ·Ό

Optional 이 λ‹΄κ³  μžˆλŠ” 객체λ₯Ό λ°˜ν™˜ν•˜λ©° Optional 이 λΉ„μ–΄μžˆλŠ” 경우 (null 인 경우) μ—λŠ” λ‹€λ₯΄κ²Œ μž‘λ™ν•œλ‹€.

  • get()

λΉ„μ–΄μžˆλŠ” Optional 객체에 λŒ€ν•΄μ„œ, NoSuchElementException 을 λ˜μ§„λ‹€.

  • orElse(T other)

λΉ„μ–΄μžˆλŠ” Optional 객체에 λŒ€ν•΄μ„œ, λ„˜μ–΄μ˜¨ 인자λ₯Ό λ°˜ν™˜ν•œλ‹€.

  • orElseGet(Supplier<? extends T> other)

λΉ„μ–΄μžˆλŠ” Optional 객체에 λŒ€ν•΄μ„œ, λ„˜μ–΄μ˜¨ ν•¨μˆ˜ν˜• 인자λ₯Ό 톡해 μƒμ„±λœ 객체λ₯Ό λ°˜ν™˜ν•œλ‹€. orElse(T other) 의 게으λ₯Έ 버전이닀.

λΉ„μ–΄ μžˆλŠ” κ²½μš°μ—λ§Œ ν•¨μˆ˜κ°€ 호좜되기 λ•Œλ¬Έμ— orElse(T other) 의 λŒ€λΉ„ μ„±λŠ₯μƒμ˜ 이점을 κΈ°λŒ€ν•  수 μžˆλ‹€.

  • orElseThrow(Supplier<? extends X> exceptionSupplier)

λΉ„μ–΄μžˆλŠ” Optional 객체에 λŒ€ν•΄μ„œ, λ„˜μ–΄μ˜¨ ν•¨μˆ˜ν˜• 인자λ₯Ό 톡해 μƒμ„±λœ μ˜ˆμ™Έλ₯Ό λ˜μ§„λ‹€.

Stream 처럼 μ‚¬μš©

  • map()
public String getCityOfMemberFromOrder(Order order) {
  return order.getMember().getAddress().getCity();
}

μœ„ μ½”λ“œλ₯Ό μ•„λž˜μ™€ 같이 λ³€κ²½

public String getCityOfMemberFromOrder(Order order) {
  return Optional.ofNullable(order)
    .map(Order::getMember)
    .map(Member::getAddress)
    .map(Address::getCity)
    .orElse("Seoul");
}
  • filter()
public Member getMemberIfOrderWithin(Order order, int min) {
  if (order != null && order.getDate().getTime() > System.currentTimeMillis() - min * 1000) {
    return order.getMember();
  }
}

μœ„μ™€ 같이 null μ²˜λ¦¬ν•˜λŠ” λΆ€λΆ„ ν˜Ήμ€ boolean 으둜 μ²΄ν¬ν•˜λŠ” 뢀뢄을 μ•„λž˜μ™€ 같이 λ³€κ²½

public Optional<Member> getMemberIfOrderWithin(Order order, int min) {
  return Optional.ofNullable(order)
    .filter(o -> o.getDate().getTime() > System.currentTimeMillis() - min * 1000)
    .map(Order::getMember);
}
  • isParent()

값이 λΉ„μ–΄μžˆλŠ”μ§€ 체크

Optional<String> type = Optional.of(null);

if (type.isParent()) {
  System.out.println("type is null");
}
  • ifPresent

νŠΉμ • κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜λŠ” λŒ€μ‹ μ— Optional 객체가 감싸고 μžˆλŠ” 값이 μ‘΄μž¬ν•  경우 싀행될 λ‘œμ§μ„ ν•¨μˆ˜ν˜• 인자둜 λ„˜κΈ°λŠ”κ²ƒμ΄ κ°€λŠ₯ν•˜λ‹€.

마치 비동기 λ©”μ„œλ“œμ˜ 콜백 ν•¨μˆ˜μ²˜λŸΌ λ™μž‘λœλ‹€.

Optional<String> maybeCity = getAsOptional(cities, 3); // Optional

maybeCity.ifPresent(city -> {
  System.out.println("length : " + city.length());
});