출처 ‣ 더 자바 Java 8, 백기선 / 인프런
Optional = 비어있을 수도 있고, 어떠한 값 하나만 담고 있을수도 있는 인스턴스의 타입
→ Optional이 객체를 감싸는 구조이다.
/* OnlineClass.java */
...
public Progress progress;
public Progress getProgress() {
return progress;
}
public void setProgress(Progress progress) {
this.progress = progress;
}
...
/* OptionalTestApp.java */
// 이슈상황 → 참조형 멤버변수 사용시 초기화 되지 않아 null값을 참조 할수 있다.
OnlineClass spring_boot = new OnlineClass(1, "spring boot", true);
Duration studyDuration = spring_boot.getProgress().getStudyDuration();// NullPointExecption 발생
System.out.println(studyDuration);
// 방법1. 사전에 null을 체크 → 에러에 대한 스택트레이스를 뽑는것 또한 리소스 낭비이다.
public Progress getProgress() {
if (progress == null) {
throw new IllegalStateException();
}
return progress;
}
// 방법2. 클라이언트 코드에서 null을 확인 → 이는 클라이언트에서 노친다면 에러가 발생할 수 있다.
Progress progress = spring_boot.getProgress();
if (progress != null) {
System.out.println(progress.getStudyDuration());
}
/* OnlineClass.java */
...
// getter의 리턴타입을 optional로변경 *Optional을 리턴타입에만 사용함을 권장!
public Optional<Progress> getProgress() {
return Optional.ofNullable(progress);
}
...
// setter에 사용시
public void setProgress(Optional<Progress> progress) {
progress.ifPresent(p -> this.progress = p);
}
// 이와 같이 사용한다면 결국, null.ifPresent(...) → NullPointExecption이 발생. Optional사용의 의미가 없다.
객체.setProgress(null);
Map 인터페이스의 중요 특징 = key는 null일수가 없다.
근데 Optional으로 key가 비어있을 수 있다는건 말이 안된다.
Optional.of(10); // 내부에서 boxing, unboxing이 이루어진다. → 성능에 좋지 않다.
OptionalInt.of(10); // 각 primitive타입에 맞는 클래스를 제공하므로 이를 사용하는것 권장
public Optional<Progress> getProgress() {
return null;
}
컨테이너 성격의 인스턴스들을 이미 비어있다는것을 표현할 수 있다.
그러므로 Optional로 감싸면 두번 감싸는 형태가 되는것이다. → 무의미
get() : 가급적 사용을 하지 않는것을 권장. null일경우 NoSuchElementException 발생
ifPresent(Consumer) : 값이 있는경우 값을 가지고 ~를 하라
orElse(T) : 값이 있으면 가져오고 없는 경우에 ~를 리턴하라 *T는 인스턴스 타입
orElseGet(Supplier) : 값이 있으면 가져오고 없는 경우에 ~를 하라
orElseThrow() : 값이 있으면 가져오고 없는 경우 에러를 던져라
package java8.optionaltest;
import java8.domain.OnlineClass;
import java8.domain.Progress;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTestApp {
public static void main(String[] args) {
List<OnlineClass> springClasses = new ArrayList<>();
springClasses.add(new OnlineClass(1, "spring boot", true));
springClasses.add(new OnlineClass(2, "spring data optional", true));
// optional을 리턴하는 stream의 메서드는 종료형 operation이라 할수잇다.
Optional<OnlineClass> optional = springClasses.stream()
.filter(oc -> oc.getTitle()
.startsWith("optional"))
.findFirst();
boolean present = optional.isPresent();
boolean empty = optional.isEmpty(); // Java11 부터 제공
System.out.println(present); // false
System.out.println(empty); // true;
/**
* optional 내부값 가져오기
* */
// 1. get()
//OnlineClass onlineClass = optional.get(); // NoSuchElementException 발생
// 2. isPresent() + get() = 먼저 확인후 꺼낸다 -> 번거롭다
/*if (optional.isPresent()) {
OnlineClass onlineClass = optional.get();
System.out.println(onlineClass.getTitle());
}*/
// 3. ifPresent(Consumer) = 값이 있는 경우만 함수가 동작한다!
optional.ifPresent(oc -> System.out.println(oc.getTitle()));
// 4. orElse() = 값이 없는 경우 리턴할 객체를 넣어줌 (이는 기존 Optional이 감싸고 있던 인스턴스 타입이다.)
// BUT, 이경우 값이 있던 없던 createNewClass()가 실행은 됨. (리턴은 있는경우 그게 리턴되나 createNewClass 함수는 실행이됨)
// 이미 만들어진 인스턴스를 사용한다면 orElse 함수로 만들어 줘야한다면, orElseGet권장!
OnlineClass onlineClass = optional.orElse(createNewClass());
System.out.println(onlineClass.getTitle());
// 5. orElseGet(Supplier) = 값이 없는 경우만 createNewClass 실행
OnlineClass onlineClass1 = optional.orElseGet(OptionalTestApp::createNewClass);
System.out.println(onlineClass1.getTitle());
// 6. orElseThrow(Supplier)
OnlineClass onlineClass2 = optional.orElseThrow(() -> {
return new IllegalArgumentException();
});
// 메소드 레퍼런스 사용
OnlineClass onlineClass3 = optional.orElseThrow(IllegalArgumentException::new);
// 7. Optional filter(Predicate) = Optional타입이 리턴됨
Optional<OnlineClass> onlineClass4 = optional.filter(oc -> oc.getId() > 10);
// 8. Optional map(Function) = map으로 변환한 타입을 Optional로 감싸서 리턴함
Optional<Integer> integer = optional.map(OnlineClass::getId);
// Optional을 리턴하는 경우 굉장히 복잡해짐
Optional<Optional<Progress>> progress = optional.map(OnlineClass::getProgress);
Optional<Progress> progress1 = progress.orElse(Optional.empty());
// 9. Optional flatMap(Function)
Optional<Progress> progress2 = optional.flatMap(OnlineClass::getProgress);
}
private static OnlineClass createNewClass() {
System.out.println("createNewClass 함수 실행");
return new OnlineClass(10 ,"New class", false);
}
}
https://github.com/eunsolJo/algorithm-study/commit/972922871567e3d8c7471b2040af692c03adec63