Menu

[java] Optional 사용

Optional

  • java 8 에 추가

  • 있거나 없는 값 표현

  • null 대체

생성

  • Optional.of(): null 이 아닌 값

Optional<String> opt = Optional.of("value");

Optional.of(null) -> NullPointerException !
  • Optional.ofNullable(): null이 가능한 값으로 생성

Optional<String> opt = Optional.ofNullable(someValue);
  • Optional.empty(): 빈 값

Optional<String> opt1 = Optional.empty();
Optional<String> opt2 = Optional.ofNullable(null);

조회

  • get()

    • 값이 없는 Optional에 get() 을 실행하면 NoSuchElementException 발생

Optional<String> opt = Optional.of("value");
String str = opt.gt(); // str: "value"

Optional<String> opt1 = Optional.empty();
opt1.get(); // NoSuchElementException

값 존재 확인

  • isPresent(): 값이 있는지 확인

  • isEmpty(): 값이 비었는지 확인 (java 11)

Optional<String> opt = Optional.of("value");
opt.isPresent(); // true
opt.isEmpty(); // false

Optional<String> opt1 = Optional.empty();
opt`.isPresent(); // false
opt`.isEmpty(); // true

값이 있으면 실행

  • ifPresent() : if 문

String value1 = getValue1();
if (value1 != null) {
    doSome(value);
}

// change..

Optional<String> opt1 = getValue();
opt1.ifPresent(value1 -> doSome(value1));
  • ifPresentOrElse(): if-else 문

String value2 = getValue();
if (value2 != null) {
    doSome(value2);
} else {
    doOther();
}

// change..

Optional<String> opt2 = getValue();
opt2.ifPresentOrElse(
    value2 -> doSome(value2),
    () -> doOther()
);

값이 없을 때 다른 값 사용

String value = getValue();
String result = value == null ? "default" : value;
  • orElse()

    • 값이 있으면 그 값을 return

    • 값이 없으면 인자로 전달한 값 전달 (아래 예제에선 default return)

Optional<String> opt = getValue();
String result = opt.orElse("default");
// String result = opt.orElse(null);
  • orElseGet()

    • orElase 와 거의 동일하나 값일 없을 경우 값 대신에 실행할 method 를 지정

Optional<String> opt = getValue();
// orElseGet(Supplier<? extends T>)
String result = opt.orElseGet(() -> "default");
  • or()

    • 값이 없을 경우 사용할 Optional return

Optional<String> opt = getValue();
// or(Supplier<? extends Optional<? extends T>>)
Optional<String> result = opt.or(() -> Optional.or("default"));
  • orElseThrow

    • 값이 있으면 해당 값 return, 없으면 exception 발생

Member m = repository.findById("id");
if ( m == null) {
  throw new NoMemberException();
}
m.doSomething();

// change..

Optional<Member> opt = repository.findByid("id");
Member m = opt.orElseThrow(() -> new NoMemberException());
m.doSomething();

map

  • map에 전달 받은 함수를 실행해서 값을 변환한 Optional return

  • map은 값이 없으면 빈 Optional return

Member m = repository.findById("id");
LocalDate birth = m.getBirthday();
int passedDays = cal(birth);

// change...

Optional<Member> memOpt = repository.findByid("id");
Optional<LocalDate> birthOpt = memOpt.map(mem -> mem.getBirthday());
Optional<Integer> pdOpt = birthOpt.map(birth -> cal(birth));

// 한번에 변환
Optional<Integer> pdOpt2 = memOpt.map(mem -> mem.getBirthday()).map(birth -> cal(birth));

// 빈 Optionaal 은 map 으로 전달한 함수를 실행하지 않고 빈 Optional 리턴
Optional<String> empty = Optional.empty();
Optional<String> empty2 = empty.map(str -> str.toUpperCase());
// empty2.isEmpty() -> true

flatMap

  • flatMap에 전달 받은 함수 이용 값을 변환한 Optional return

    • flatMap에 전달한 함수는 return typeOptional

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

Optional<Member> memOpt = repository.findByid("id");
Optional<LocalDate> birthOpt = memOpt.flatMap(mem -> Optional.orNullable(mem.getBirthday()));
Optional<Integer> pdOpt = birthOpt.flatMap(birth -> calOptional<birth));

// flatMap 은 map 을 사용하였을 경우 Optional<Optional<LocalDate>> 와 같은 자료형의 Optional 을 하나 뺴주는 역할.

filter

  • 조건을 충족하면 값 그대로 return

  • 조건을 충족하지 못하면 빈 Optional return

String str = getStringValue();
if (str != null && str.length() > 3) {
  System.out.println("str: " + str);
}

// change 1
Optional<String> strOpt1 = getStringValue();
Optional<String> filtered1 = strOpt.filter(str -> str.length() > 3);
filtered1.isPresent(str -> System.out.println("str1: " + str));

// change 2
Optional<String> strOpt2 = getStringValue();
Optional<String> filtered2 = strOpt
                                .filter(str -> str.length() > 3)
                                .ifPresent(str -> System.out.println("str2: " + str2));

두 개의 Optional 조합

  • 예제 소스

Member m = repository.findByid("id");
if (m == null) { return null; }

Company c = getCompany(m);
if (c == null) { return null; }

Card card = createCard(m, c);
return card;
  • 변환 1

Optional<Member> memOpt = repository.findByid("id");
Optional<Company> comOpt = memOpt.map(mem -> getCompaany(mem));

Optional<Card> card = memOpt.flatMap(
    mem -> comOpt.map(com -> createCard(mem, com);
);
  • 변환 2

Optional<Member> memOpt = repository.findByid("id");
Optional<Card> cardOpt = memOpt.flatMap(mem -> {
    Optional<Company> comOpt = getCompanyOptional(mem));
    return comOpt.map(com -> createCard(mem, com));
});

정리

  • isPresent() 사용하면 -> null 사용할 때 유사한 구조

  • 대신 map, flatMap, filter, orElse, or, ifPresent 등 익숙해지기

  • Optional을 조금 더 Optional 답게

출처 & 참고