Optional 소개
자바 프로그래밍에서 NullPointerException과 같은 에러들을 종종 볼 수 있다. 이러한 에러의 경우 컴파일 에러가 아닌 런타임 에러로 프로젝트에서 발생하면 치명적일 수 있는 에러이다.
보통 NullPointerException의 경우 아래와 같은 상황에서 발생한다.
- Null 리턴 (설계상)
- Null 체크 실수 (설계상)
나도 코딩테스트를 보며 NullPointerException을 가끔 본다. 이럴 때마다 설계의 중요성을 한번씩 깨닫게 되는 것 같다..
하지만, 메소드에서 작업 중 특별한 상황에서 값을 제대로 리턴을 할 수 없는 경우가 있을 수 있다. 이러한 경우에는 아래와 같은 방법이 있을 것이다.
- 예외를 던진다. (비싸다.. 스택트레이스(에러가 발생하기 전까지의 콜스타일 저장)를 찍어두기때문이다.)
- 로직을 처리할 때 Exception처리는 좋은 로직이 아니다!
- null을 리턴한다. (비용 문제가 없지만, 코드를 사용하는 클라이언트 코드에서 주의해야 한다.)
- Optional을 리턴한다. (클라이언트에 코드에게 명시적으로 빈 값일 수도 있다는 것을 알려주고, 빈 값인 경우에 대한 처리를 강제한다.)
null을 리턴하는 경우 설계상 실수가 있을 수 있지만, Optional을 사용한다면 실수를 없앨 수 있다고 생각이 든다. 그렇다면 Optional을 알아보자.
Optional
- 오직 값 한개가 들어있을 수도 없을 수도 있는 컨테이너
주의할 점
- 리턴값으로만 쓰기를 권장 (메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말자.)
- 메소드 매개변수 타입) 매개변수로 받았지만, 메소드 안에서 다시 체크를 해야하기 때문에 코드가 길어지는 문제가 발생
- 맵의 키 타입) 맵 키의 특징 중 하나는 키가 Null이 아닌 것 이다. 이를 무너뜨린다..
- Optional을 리턴하는 메소드에서 null을 리턴하지 말자.
- 프리미티브 타입용 Optional은 따로 있다. OptionalInt, OptionalLong....
- 프리미티브 타입의 경우 Optional.of(10)과 같이 사용하면 박싱, 언박싱이 안에서 이루어지기 때문에 성능이 안좋다. 이를 해결하고자 OptionalInt, OptionalLong, OptionalDouble과 같은 Optional을 사용하자.
- Collection, Map, Stream Array, Optional은 Optional로 감싸지 말자.
- 이미 container의 성격을 갖고 있다. 굳이 감쌀 이유가 없다.
Optional 만들기
- Optional.of(instance)
- .of 는 instance가 Null이 무조건 아닐 때 사용
- Optional.ofNullable(instance)
- .ofNullable 은 instance가 null일 수도 있을 때 사용
- Optional.empty()
Optional에 값이 있는지 없는지 확인하기
- isPresent()
- isEmpty()
- Java 11부터 제공
Optional에 있는 값 가져오기
- get()
- 만약에 비어있는 Optional에서 무언가를 꺼낸다면??
- 런타임 에러가 발생한다. (NoSuchElementException)
Optional에 값이 있는 경우에 그 값을 가지고 ~~를 하라.
- isPresent(instance)
- ex) Spring으로 시작하는 수업이 있으면 id를 출력하라
Optional에 값이 있으면 가져오고 없는 경우에 ~~를 리턴하라.
- orElse(T)
- ex) JPA로 시작하는 수업이 없다면 비어있는 수업을 Return
Optional에 값이 있으면 가져오고 없는 경우에 ~~를 하라.
- orElseGet(Supplier)
- ex) JPA로 시작하는 수업이 없다면 새로 만들어서 Return
Optional에 갑싱 있으면 가져오고 없는 경우 에러를 던져라
- orElseThrow()
Optional에 들어있는 값 걸러내기
- Optional filter(Predicate)
Optional에 들어있는 값 변환하기
- Optional map(Function)
- Optional flatMap(Function)
- Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용하면 편리 (안에 있는 Optional이 한번 까져서 나옴)
코드로 확인해보자.
package me.qazyj.java8;
import java.time.Duration;
import java.util.*;
public class OptionalTest {
public static void main(String[] args) {
List<OnlineClass> springClass = new ArrayList<>();
springClass.add(new OnlineClass(1, "spring boot", true));
springClass.add(new OnlineClass(6, "rest api", false));
Optional<OnlineClass> findStartSpring = springClass.stream()
.filter(oc -> oc.getTitle().startsWith("spring"))
.findFirst(); // 찾은 첫번째 값을 return
System.out.println(findStartSpring.isPresent()); // id 1이 spring으로 시작함 ==>> true
//값이 있는지 없는지 확실하지 않을 때 ifPresent로 확인 후 있을 떄만 실행
//findStartSpring.ifPresent(oc -> System.out.println(oc.getTitle()));
//없으면 생성 x(있어도 생성하고 Return 함) orElse
Optional<OnlineClass> findStartJpa = springClass.stream()
.filter(oc -> oc.getTitle().startsWith("jpa"))
.findFirst(); // 찾은 첫번째 값을 return
OnlineClass onlineClass = findStartJpa.orElse(new OnlineClass(10, "New class", false));
System.out.println(onlineClass.getTitle());
// 에러
//OnlineClass onlineClass1 = findStartJpa.orElseThrow(IllegalStateException::new);
// is.empty - 비어있을 떄 true / 있을 떄 false
Optional<OnlineClass> onlineClass2 = findStartSpring.filter(oc -> oc.getClosed());
System.out.println("empty : " + onlineClass2.isEmpty());
}
}
'JAVA' 카테고리의 다른 글
[Java 8] CompletableFuture (0) | 2022.10.13 |
---|---|
[Java 8] Date와 Time (0) | 2022.10.12 |
[JAVA 8] Stream (0) | 2022.10.06 |
[JAVA 8] 인터페이스의 변화 (0) | 2022.10.06 |
[JAVA 8] 함수형 인터페이스와 람다 표현식 (0) | 2022.09.23 |