Optional 클래스는 java 8부터 지원하는 클래스로 NPE(NullPointerException)을 피하기 위해 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 도와준다.

메서드가 반환할 결과값이 ‘없음’을 명백하게 표현할 필요가 있고, null 을 반환하면 에러를 유발할 가능성이 높은 상황에서 메서드의 반환 타입으로 Optional을 사용하자는 것이 Optional을 만든 주된 목적이다.

Optional 객체의 생성

  • empty() : 빈 Optional 객체 생성
public final class Optional<T> {
		private static final Optional<?> EMPTY = new Optional();

    private final T value;

    private Optional() {
        this.value = null;
    }

    public static <T> Optional<T> empty() {
        Optional<T> t = EMPTY;
        return t;
    }
		...
}
  • Optional.of(T value) : value 값이 null이면 NullPointerException 발생
public final class Optional<T> {
 		public static <T> Optional<T> of(T value) {
        return new Optional(value);
    }

		private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
}

public final class Objects {
		public static <T> T requireNonNull(T obj) {
        if (obj == null) {
            throw new NullPointerException();
        } else {
            return obj;
        }
    }
}
  • ofNullable(T value) : value 값이 null이 아니면 value 값을 가지는 Optional 객체 반환, value 값이 null이면 빈 Optional 객체를 반환
public final class Optional<T> {
		public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
}

객체의 접근

get() 메소드를 사용하면 Optional 객체 값에 접근할 수 있다.

만약 Optional 객체에 저장된 값이 null이면, NoSuchElementException 이 발생한다.

따라서, get() 을 호출하기 전에 ifPresent() 를 호출해서 Null체크를 해야한다.

Optional<String> opt = Optional.ofNullable("Optional");

if(opt.isPresent()){
		System.out.println(opt.get());
}

//더 간단하게
opt.ifPresent(name -> System.out.println(name));
  • orElse() : 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환
  • orElseGet() : 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 lambda 표현식의 값을 반환 함.
  • orElseThrow() : 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전단될 예외를 발생시킴

바르게 쓰기

http://homoefficio.github.io/2019/10/03/Java-Optional-바르게-쓰기/

위 블로그에서 작성을 잘 해놓으셨다.

1. isPresent()-get() 대신 orElse(), orElseGet(), orElseThrow()를 사용하자

2. orElse(new …) 대신 orElseGet(() → new …) 를 사용하자.

orElse(...) 에서 인자 값은 있든 없든 무조건 수행된다. 따라서 새로운 연산을 하는 경우 orElseGet()을 써야한다. orElse(...)의 경우 새로운 연산을 하지 않고 이미 생성되었거나 이미 계산된 값일 때만 사용한다.

3. 단지 값을 얻을 목적이라면 Optional 대신 null을 비교하자

4. Optional대신 비어있는 컬렉션을 반환하자

컬렉션은 Optional로 감싸서 반환하지 말고 비어있는 컬렉션을 반환하자

return members != null ? members : Collections.emptyList();

5. Optional을 필드로 사용하지 말자

Optional 자체가 필드에 사용할 목적이 아니라 Serializable 을 구현하지 않았다.

6. Optional을 생성자나 메서드 인자로 사용하지 말자

호출되는 쪽에서 null 체크를 해주는게 바람직하다.

7. Optional을 컬렉션의 원소로 사용하지 말자

8. of(), ofNullable() 혼동 주의

of()는 null이 아님이 확실할 때만 사용하고, null이 들어올 수 있으면 ofNullalble()을 사용하자

9. Optional<T> 대신 OptionalInt, OptionalLong, OptionalDouble을 사용하자

참고 자료