[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에 값이 있으면 가져오고 없는 경우에 ~~~을 리턴하라.
- orElse(T)
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