Optional
Java8 에 추가된 새로운 인터페이스.
비어있을수도 있고, 값 하나를 담고 있을수도 있는 컨테이너 인스턴스의 타입이다.
즉, 오직 값 한 개가 들어있을 수도 없을 수도 있는 컨테이너.
자바 프로그래밍에서 NullPointerException 을 보는 이유.
- null 를 리턴하니까! ( null 이 리턴되는 자체가 문제이다. )
- null 체크를 깜빡했으니까!
메소드에서 작업 중 특별한 상황에서 값을 제대로 리턴할 수 없는 경우 선택할 수 있는 방법
- 예외를 던진다.
- 에러가 발생하게되면 자바는 스택 트레이스를 찍는데, ( 이 에러가 발생하기 전까지의 어떠한 콜 스택을 거쳐서 에러가 발생하게 되었는지에 대한 정보) 이 자체로 리소스를 사용하는거여서 부담이 가기 때문에 필요할 때에만 사용해야지 로직을 처리할때 사용하는건 비용부담이 된다.
- null 를 리턴한다.
- 비용문제는 없지만 그 코드를 사용하는 클라이언트 코드가 주의해야한다.
- Optional 을 리턴한다.
- 클라이언트에 코드에게 명시적으로 빈 값일 수도 있도있다는 걸 알려주고, 빈 값인 경우에 대한 처리를 강제한다.
주의할 것
- 리턴값으로만 쓰기를 권장한다.
- 메소드 매개변수 타입, 맵의 키의 타입, 인스턴스 필드 타입으로 쓰지 말자.
- 맵의 키 타입에 Optional 을 준다는것은 맵의 특징을 무시하는 것이다. (키는 null 일 수 없다.)
- Optional 을 리턴하는 메소드에서 null 을 리턴하지 말자.
- 리턴 할 것이 없다면 null 말고, Optional.empty( ) 를 리턴하자.
- 프리미티브 타입용 Optional 은 따로 있다. (OptionalInt, OptionalLong ...)
- 프리미티브 타입에 Optional 을 사용할 경우 박싱, 언박싱이 발생한다. OptionalInt 을 사용하자.
- Collection, Map, Stream Array, Optional 은 Optional 로 감싸지 말 것.
- 이미 해당 타입들은 비어있다는 것을 표현할 수 있는 컨테이너 성격을 가진 타입들이다.
실습
package com.study.java8to11;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalStudy {
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 dev", false));
Optional<OnlineClass> optional = springClasses.stream()
.filter(oc -> oc.getTitle().startsWith("spring")) // 있을 수도 있고 없을 수도 있어서 리턴 타입은 Optional 이다.
.findFirst();
// [1] Optional 에 값이 있는지 없는지 확인하기
boolean present = optional.isEmpty();
boolean empty = optional.isPresent();
// [2] Optional 에 있는 값 가져오기
// [2-1] 비어있는 Optional 에서 무언가 꺼낼땐 NoSuchElementeException 발생
// System.out.println(optional.get());
// [2-2] 예외처리 적용 , 하지만 이렇게 get 으로 가져오고 isPresent 로 예외처리 하는 방식은 잘 사용되지 않는다.
if (optional.isPresent()) {
System.out.println(optional.get());
}
// [3] Optional에 값이 있는 경우 그 값을 가지고 ~를 하라.
// ex) spring 으로 시작하는 수업이 있으면 id 를 출력하라
optional.ifPresent(oc -> {
System.out.println(oc.getId());
});
// [4] Optional에 값이 있으면 가져오고 없는 경우에 ~를 리턴하라
// ex) jpa 로 시작하는 수업이 없다면 비어있는 수업을 리턴하라.
// orElse(T) 함수 구현체가 아닌, 인스턴스를 매개변수로 주어야한다.
// orElse 는 optional 에 값이 있던, 없던 무조건 실행된다.
OnlineClass onlineClass = optional.orElse(createNewClasses());
System.out.println(onlineClass.getTitle());
// [5] Optional에 값이 있으면 가져오고 없는 경우에 ~를 하라.
// ex) JPA로 시작하는 수업이 없다면 새로 만들어서 리턴하라.
// orElseGet(Supplier) supplier 로 구현해주면 된다. (람다 혹은 메서드 레퍼런스)
// orElseGet 는 optional 에 값이 없는 경우에만 실행된다.
// orElse 는 이미 만들어져 있는 인스턴스, 상수 들을 참고해서 사용할땐 orElse 가 적합
// orElseGet 동적으로 무언가 작업해서 만들어내야 한다면 orElseGet 이 적합
OnlineClass onlineClass2 = optional.orElseGet(OptionalStudy::createNewClasses);
System.out.println(onlineClass2.getTitle());
// 무언가 만들어줄 수 없는상황에는 orElseThrow
// [6] Optional에 값이 있으면 가져오고 없는 경우 에러를 던져라.
// default 로는 NoSuchElementException 을 던지지만, 원하는 에러가 있을경우 supplier 로 구현해주면 된다.
OnlineClass onlineClass3 = optional.orElseThrow(IllegalArgumentException::new);
// [7] Optional에 들어있는 값 걸러내기
// 값이 있다는 가정하에 이벤트가 일어나며, 값이 없을경우 아무 일도 일어나지 않는다.
Optional<OnlineClass> onlineClass4 = optional.filter(OnlineClass::isClosed);
System.out.println(onlineClass4.isEmpty()); // true
// [8] Optional에 들어있는 값 변환하기
// [8-1] Optional map(Function)
Optional<Integer> integer = optional.map(OnlineClass::getId); // id 타입을 담고있는 Optional 반환
// [8-2] Optional flatMap(Function): Optional 안에 들어있는 인스턴스가 Optional인 경우 사용하면 편리하다.
Optional<Progress> progress = optional.flatMap(OnlineClass::getProgress);
// [8-3] Optional 안에 있는 값이 Optional 인데 map 을 사용하게 될 경우, 아래와 같이 두번 작업을 해야한다.
Optional<Optional<Progress>> progress1 = optional.map(OnlineClass::getProgress);
Optional<Progress> progress2 = progress1.orElse(Optional.empty());
// 무조건 .get 으로 꺼내기 보다는 여태 배운 메소드를 활용하여 적용하자.
}
private static OnlineClass createNewClasses() {
System.out.println("creating new online class");
return new OnlineClass(10, "new class", false);
}
}
참고
- https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
- https://www.oracle.com/technical-resources/articles/java/java8-optional.html
- 이팩티브 자바 3판, 아이템 55 적절한 경우 Optional을 리턴하라.
- 백기선 - 더 자바, Java8
'프로그래밍 > Java' 카테고리의 다른 글
CompletableFuture (1) - 자바 Concurrent 프로그래밍 소개 (0) | 2022.05.06 |
---|---|
Date와 Time API (0) | 2022.05.05 |
Stream (0) | 2022.05.04 |
Lombok 동작원리 (0) | 2022.05.03 |
다이나믹 프록시 (0) | 2022.05.03 |