LeeDiculous
article thumbnail
Published 2022. 12. 30. 11:45
Optional 언어 ⏎/java

Optional

개발자가 가장 곤란한 예외 중 하나인 NullPointerException(NPE). 이 예외를 피하고 null 을 체크하는 로직을 줄이기 위해 빈 값일 때 null 대신 초기값을 사용하길 권장하곤 합니다.

자바 8에서 추가된 Optional<T> 클래스를 이용해서 NPE 을 방지할 수 있습니다. Optional<T> 클래스는 한 마디로 null 이 올 수 있는 값을 감싸는 래퍼 클래스로 참조하더라도 null 이 일어나지 않도록 해주는 클래스입니다.

public final class Optional<T> {

  // If non-null, the value; if null, indicates no value is present
  private final T value;

  ...
}

 

생성하기

먼저 다음과 같이 빈 객체를 생성할 수 있습니다.

Optional<String> optional = Optional.empty();
System.out.println(optional); // Optional.empty

System.out.println(optional.isPresent()); // false

혹은 null 이 올 수 있는 값을 Optional<T> 로 감싸서 생성할 수 있습니다. orElse 또는 orElseGet 메소드를 이용해서 값이 없는 경우라도 안전하게 값을 가져올 수 있습니다.

다음 예제에서 getString 메소드가 리턴하는 값이 null 일 수도 있습니다. 하지만 Optional<T> 로 감싸면 됩니다.

// Optional 안에는 값이 있을 수도 있고 빈 객체일 수도 있다.
Optional<String> optional = Optional.ofNullable(getString());

String result = optional.orElse("other"); // 값이 없다면 "other" 를 리턴

 

사용하기

사용법은 간단합니다. 먼저 자바 8 이전에는 다음과 같이 null 체크가 필요했습니다.

// 자바 8 이전
List<String> list = getList();
List<String> listOpt = list != null ? list : new ArrayList<>();

Optional<T>Lambda 를 이용하면 좀 더 간단하게 표현할 수 있습니다.

List<String> listOpt = Optional.ofNullable(getList()).orElseGet(() -> new ArrayList<>());

다음 코드는 null 체크 때문에 지저분해진 코드입니다.

User user = getUser();
if (user != null) {
  Address address = user.getAddress();
  if (address != null) {
    String street = address.getStreet();
    if (street != null) {
      return street;
    }
  }
}
return "주소 없음";

위 코드처럼 null 이 올 수 있는 값을 하나하나 체크한다면 매우 지저분해지고 귀찮은 일이 될 것입니다.

map메소드는 해당 값이 null 이 아니면 mapper 를 이용해 계산한 값을 저장하는 Optional객체 를 리턴합니다. 만약 값이 null이라면 빈 Optional 객체 를 리턴합니다.

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)

map 메소드를 이용해서 간단하게 표현해보겠습니다.

Optional<User> user = Optional.ofNullable(getUser());
Optional<Address> address = user.map(User::getAddress);
Optional<String> street = address.map(Address::getStreet);
String result = street.orElse("주소 없음");

// 다음과 같이 축약해서 쓸 수 있다.
user.map(User::getAddress)
    .map(Address::getStreet)
    .orElse("주소 없음");

만약 위 예제에서 getAddressgetStreetOptional<T>객체를 리턴한다면 flatMap 메소드를 사용할 수도 있습니다.

Optional<User> user = Optional.ofNullable(getUser());
String result = user
         .flatMap(User::getAddress)
         .flatMap(Address::getStreet)
         .orElse("주소 없음");

다음 예제는 NPE 을 핸들링하는 예제입니다.

String value = null;
String result = "";
try {
  result = value.toUpperCase();
} catch (NullPointerException e) {
  throw new CustomExcpetion();
}

위와 같은 코드는 Optional<T> 을 이용하면 다음과 같이 표현할 수 있습니다.

String value = null;
Optional<String> valueOpt = Optional.ofNullable(value);
String result = valueOpt.orElseThrow(CustomExcpetion::new).toUpperCase();

'언어 ⏎ > java' 카테고리의 다른 글

정규표현식  (0) 2023.02.07
Stream  (0) 2022.12.30
더블콜론과 람다  (0) 2022.12.30
람다(Lambda)  (0) 2022.12.30
XML parsing  (0) 2022.12.29
profile on loading

Loading...