# Table of Contents

# Optional API

Java 8부터 도입된 Optional API를 사용하면 NullPointException을 더 쉽게 핸들링할 수 있다.

# NullPointException

Java 언어로 개발을 할 때 가장 많이 발생하는 예외 중 하나가 바로 NullPointException이다.

Person person = null;
person.getName();
Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:16)

NullPointException을 피하기 위한 조건문은 코드를 지저분하게 한다.

Person person = null;
if (person != null) {
    person.getName();
}

# Optional

Optional은 Java 8에서 추가된 기능으로 null이 올 수 있는 객체를 감싼 Wrapper 클래스다. 객체는 Optional의 value 속성에 저장된다.

public final class Optional<T> {
    private final T value;

    // ...
}

Optional을 다음과 같이 생성한다.

Person person = null;
Optional<Person> optional = Optional.ofNullable(null);

이제 null이 아닐 때만 값에 접근할 수 있다.

optional.ifPresent(p -> p.getName());

이제 자세한 Optional 사용법에 대해 알아보자.

# Optional 생성하기

# Optional.empty()

empty()는 값이 빈 Optional을 생성한다.

Optional<Person> optional = Optional.empty();

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

# Optional.of()

of()에 null일 가능성이 없는 값을 전달하여 Optional을 생성한다.

Person p = new Person("Paul");

Optional<Person> optional = Optional.of(p);

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

of()는 값이 없는 경우 NullPointException이 발생시킨다.

Person p = null;
Optional<Person> optional = Optional.of(p);     // NullPointerException

# Optional.ofNullable()

ofNullable()은 null일 가능성이 있는 객체를 전달하여 Optional을 생성한다.

Optional<Person> optional = Optional.ofNullable(null);
System.out.println(optional.isPresent());   // false
Person person = new Person("Paul");
Optional<Person> optional = Optional.ofNullable(person);
System.out.println(optional.isPresent());   // true

# Null 체크

Optional 클래스에는 Null 체크를 위한 메소드가 존재한다.

# isPresent()

isPresent()를 사용하면 객체가 null인지 확인할 수 있다.

Optional<Person> optional = Optional.ofNullable(null);
System.out.println(optional.isPresent());   // false
Person person = new Person("Paul");

Optional<Person> optional = Optional.ofNullable(person);
System.out.println(optional.isPresent());   // true

# ifPresent()

ifPresent()는 객체가 null이 아닐 때 람다식을 실행한다.

Person person = new Person("Paul");

Optional.ofNullable(person)
    .ifPresent((p -> System.out.println(p.getName())));
Paul

객체가 null이면 람다식을 실행하지 않는다.

Person person = null;

Optional.ofNullable(person)
    .ifPresent((p -> System.out.println(p.getName())));

# 값 가져오기, 예외 처리하기

# get()

get()을 사용하면 Optional이 가지고 있는 객체를 가져올 수 있다.

String str = Optional.ofNullable("Hello").get();

만약 값이 없다면 NoSuchElementException이 발생한다.

String str = Optional.ofNullable(null).get();   // NoSuchElementException

# orElse()

orElse()는 Optional이 비어있다면 orElse()로 지정한 값을 반환한다.

String str = "Something";

String value = Optional.ofNullable(str)
        .orElse("Another thing");

System.out.println(value);  // Something
String str = null;

String value = Optional.ofNullable(str)
        .orElse("Another Thing");

System.out.println(value);  // Another Thing

# orElseGet()

orElseGet()는 Optional이 비어있다면 람다식을 실행하고 람다식의 반환값을 반환한다.

String str = "Something";

String value = Optional.ofNullable(str)
        .orElseGet(() -> "Another thing");

System.out.println(value);  // Something
String str = null;

String value = Optional.ofNullable(str)
        .orElseGet(() -> "Another thing");

System.out.println(value);  // Another thing

# orElseThrow()

orElseThrow()는 Optional이 비어있다면 예외를 발생시킨다.

UserEntity user = userRepository.findOneByName(username)
    .orElseThrow(() -> new UsernameNotFoundException(username));

메소드 참조를 사용할 수도 있다.

String str = "Something";

String value = Optional.ofNullable(str)
        .orElseThrow(NoSuchElementException::new);

System.out.println(value);      // Something
String str = null;

String value = Optional.ofNullable(str)
        .orElseThrow(NoSuchElementException::new);  // Exception in thread "main" java.util.NoSuchElementException

System.out.println(value);

# 필터링

filter()을 사용하면 객체를 필터링할 수 있다.

Optional.of("ABCD")
    .filter(v -> v.startsWith("AB"))
    .ifPresent(value -> System.out.println(value));
ABCD
Optional.of("ABCD")
    .filter(v -> v.startsWith("XY"))
    .ifPresent(value -> System.out.println(value));

# 변환

# map()

map()을 사용하면 객체를 변환할 수 있다.

Optional.of("ABCD")
    .map(value -> value.toLowerCase())
    .ifPresent(value -> System.out.println(value));
abcd

위 코드는 메소드 참조을 사용하여 다음과 같이 단축할 수도 있다.

Optional.of("ABCD")
    .map(String::toLowerCase)
    .ifPresent(System.out::println);

# flatMap()

flatMap()을 사용하면 다른 Optional로 반환할 수 있다.

Optional.of("ABCD")
    .flatMap(value -> Optional.of(value.toLowerCase()))
    .ifPresent(System.out::println);    // abcd

# 원시타입 옵셔널

제너릭과 Wrapper 클래스를 사용하면 원시 타입의 옵셔널을 생성할 수 있다.

Optional<Integer> optional = Optional.of(1);

optional.ifPresent(System.out::println);    // 1

그러나 제네릭과 Wrapper 클래스를 사용하지 않고도 원시 타입의 옵셔널을 생성할 수도 있다.

OptionalInt optional = OptionalInt.of(1);

optional.ifPresent(System.out::println);    // 1

제공되는 원시 타입의 옵셔널은 다음과 같다.

  • OptionalInt
  • OptionalLong
  • OptionalDouble