[JAVA8] Optional

오직 값 한 개가 들어있을 수도 없을 수도 있는 컨테이너

자바 8부터 Optional을 리턴한다.

(클라이언트 코드에게 명시적으로 빈 값일 수도 있다는 걸 알려주고 , 빈 값인 경우에 대한 처리를 강제한다.)

 

 

1. Optional만들기

  • Optional.of()
  • Optional.ofNullable()
  • Optional.empty()

2. Optional에 값이 있는지 없는지 확인하기

  • isPresent()
  • isEmpty() - JAVA11부터 제공

3. Optional에 있는 값 가져오기

  • get()

4. Optional에 값이 있는 경우에 그 값을 가지고 ~~~을 하라.

  • ifPresent(Consumer)

5. Optional에 값이 있으면 가져오고 없는 경우에 ~~~을 리턴하라.

6. Optional에 값이 있으면 가져오고 없는 경우에 ~~~을 하라.

  • orElseGet(Supplier)

7. Optional에 값이 있으면 가져오고 없는 경우에 에러를 던져라.

  • orElseThrow()

8. Optional에 들어있는 값 걸러내기

  • Optional filter(Predicate)

9. Optional에 들어있는 값 변환하기

  • Optional map(Function)
  • Optional flatMap(Function) : Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용하면 편리하다.

 

OnlineClass (Entity)

더보기
public class OnlineClass {
    private Integer id;
    private String title;
    private boolean closed;
    public Progress progress;

    public OnlineClass(Integer id, String title, boolean closed) {
        this.id = id;
        this.title = title;
        this.closed = closed;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isClosed() {
        return closed;
    }

    public void setClosed(boolean closed) {
        this.closed = closed;
    }

    public Optional<Progress> getProgress() {
//        return Optional.of(progress);     // null이 들어오면 예외가 난다.
        return Optional.ofNullable(progress);

    }

    public void setProgress(Progress progress) {
        this.progress = progress;
    }
    @Override
    public String toString() {
        return "OnlineClass{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", closed=" + closed +
                '}';
    }
}

Progress (Entity)

더보기
public class Progress {
    private Progress studyDuration;
    private boolean finished;

    public Progress getStudyDuration() {
        return studyDuration;
    }
    public void setStudyDuration(Progress studyDuration) {
        this.studyDuration = studyDuration;
    }
}

AppForOptionalTest

public class AppForOptionalTest {
    public static void main(String[] args) {
        List<OnlineClass> springClasses = new ArrayList<>();
        springClasses.add(new OnlineClass(1 , "spring boot" , true));
        springClasses.add(new OnlineClass(5 , "rest api development" , false));

        // Optional로 리턴되는 스트림의 종료 오퍼레이션이 존재한다.
        Optional<OnlineClass> spring = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("spring"))
                    .findFirst();

        // 존재하는지 ?
        boolean isPresent = spring.isPresent();
        System.out.println(isPresent);    // true

        // 비었는지 ?
        boolean isEmpty = spring.isEmpty();    // JAVA11부터
        System.out.println(isEmpty);   // false

        // 값 가져오기 (값이 있을 때)
        OnlineClass get = spring.get();
        System.out.println(get);

        // 값이 없을 때 get을 바로 하게 되면 예외 발생
        OnlineClass onlineClass2 = spring.get();
        System.out.println(onlineClass2);
        // java.util.NoSuchElementException
        // ifPresent를 사용하여 값이 있는지 없는지 체크하여야 한다.
        spring.ifPresent(oc -> System.out.println(oc.getTitle()));

        // orElse는 Optional 값이 있든 없든 orElse(...) 안의 ...은 무조건 실행된다.
        OnlineClass orElse1 = spring.orElse(createNewClass());
        OnlineClass orElse2 = spring.orElse(new OnlineClass(11 , "NewClass2" , false));

        // orElseGet은 Optional 값이 있으면 orElseGet(...) 안의 ...은 실행되지 않는다.
        // 람다 표현식
        OnlineClass orElseGet1 = spring.orElseGet(() -> createNewClass());
        // 메서드 레퍼런스
        OnlineClass orElseGet2 = spring.orElseGet(AppForOptionalTest::createNewClass);

        // orElseThrow
        // 값이 존재 하지 않으면 java.util.NoSuchElementException 예외를 발생한다.
        OnlineClass orElseThrow1 = spring.orElseThrow();
        // 메서드 레퍼런스 (예외를 지정할 수도 있다.)
        OnlineClass orElseThrow2 = spring.orElseThrow(IllegalArgumentException::new);

        // filter
        Optional<OnlineClass> filter = spring.filter(oc -> !oc.isClosed());

        // map
        // 메소드 레퍼런스
        Optional<String> strMap = spring.map(OnlineClass::getTitle);

        // flatMap
        // OnlineClass getProgress는 Optional<Progress>를 반환한다.
        // 그러면 Optional<Optional<Progress>>가 된다.
        // 이걸 유용하게 꺼낼 수 있게 해주는 메소드 flatMap이다.
        Optional<Progress> flatMap = spring.flatMap(OnlineClass::getProgress);

        // flatMap을 사용하지 않으면 이렇게 2번 체크해야 한다.
        Optional<Optional<Progress>> progress1 = spring.map(OnlineClass::getProgress);
        Optional<Progress> progress2 = progress1.orElse(Optional.empty());
    }
    private static OnlineClass createNewClass(){
        return new OnlineClass(10 , "New Class" , false);
    }
}

 

 

주의할 것

  • 리턴값으로만 쓰기를 권장한다.(메서드 매개변수 타입 , 맵의 키 타입 , 인스턴스 필드 타입으로 쓰지 말자.)
  • Optional을 리턴하는 메서드에서 null을 리턴하지 말자.
  • 프리미티브 타입용 Optional이 따로 있다.
    •  OptionalInt , OptionalLong , ...
  • Collection , Map , Stream Array , Optional은 Optional로 감싸지 말 것

 

 

 

 

더 자바, Java 8 - 인프런

자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이

www.inflearn.com

 

 

 

 

 

읽어보기

 

Java Optional 바르게 쓰기

Java Optional 바르게 쓰기Brian Goetz는 스택오버플로우에서 Optional을 만든 의도에 대해 다음과 같이 말했다. … it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. Our intention w

homoefficio.github.io

'기록 > JAVA' 카테고리의 다른 글

Thread  (0) 2021.01.16
[JAVA8] Date 와 Time  (0) 2021.01.14
[JAVA8] Stream API 사용 예제  (0) 2021.01.10
[JAVA8] Stream  (0) 2021.01.10
[JAVA8] API의 기본 메서드와 스태틱 메서드  (0) 2021.01.10