온라인 학습/더 자바, Java 8 강의

4. Optional

2022. 11. 11. 11:50

<1> Optional 소개

 

  • NullPointerException을 종종 보게 되는 이유
    : null을 리턴하거나 null 체크를 잊기 때문이다.

 

예)

기본값이 null인 progress를 리턴하는 getProgress

 

NullPointerException 에러 발생

 

null 체크

 

null값일 경우 NullPointerException 에러가 발생하므로 위와 같이 if문 조건 걸어서 null 체크를 하던 게 지금까지 하던 방식이다.

그러나 이런 방식은 애초에 null을 리턴하거나 null 체크를 잘 잊기 때문에 에러를 만들기 좋다.

 

 

  • 메소드에서 작업 중 특별한 상황에서 값을 제대로 리턴할 수 없는 경우 선택할 수 있는 방법
    • 예외를 던진다. -> 비싸다, 스택트레이스를 찍어두니까.
    • null을 리턴한다.  -> 비용 문제가 없지만 그 코드를 사용하는 클리어인트 코드가 주의해야 한다.
    • (자바 8부터) Optional을 리턴한다. -> 클라이언트에 코드에게 명시적으로 빈 값일 수도 있다는 걸 알려주고, 빈 값인 경우에 대한 처리를 강제한다.

 

 

  • Optional
    : 오직 값 한 개들어있을 수도 없을 수도 있는 컨네이너

 

  • Optional로 객체를 감싸서 사용하게 되면
    • NullPointerException을 유발할 수 있는 null을 직접 다루지 않아도 된다.
    • 수고롭게 null 체크를 직접 하지 않아도 된다.
    • 명시적으로 해당 변수가 null일 수도 있다는 가능성을 표현할 수 있다. (따라서 불필요한 방어 로직을 줄일 수 있다.)

 

  • Optional 클래스의 3가지 정적 팩토리 메소드
    1. Optional.empty() : null을 담고 있는(비어 있는) 옵셔널 객체 생성.
    2. Optional.of(value) : null이 아닌 객체를 담고 있는 옵셔널 객체 생성. null이 넘어오면 NPE 던진다.
    3. Optional.ofNullable(value) : null인지 아닌지 확신할 수 없는 객체를 담는 옵셔널 객체 생성.

 

Public Optional<Progress> getProgress() {
	return Optional.ofNullable(progress);
}

 

 

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

 

//Optional.of(10);  // 그냥 감싸면 박싱,언박싱 하느라 성능 안 좋아짐.
OptionalInt.of(10);
Public Optional<Progress> getProgress() {
	//return null;  // null을 리턴하지 말 것.
	return Optional.empty();  //리턴할 게 없으면 empty를 해라
}

 

 

 


<2> Optional API

 

 

 

  • Optional 만들기
    • Optional.of()
    • Optional.ofNullable()
    • Optional.empty()

 

 

 

  • Optional에 값이 있는지 없는지 확인하기
    • isPresent()
    • isEmpty() (Java 11부터 제공)
boolean present = spring.isPresent();
System.out.println(present);  // 값이 있으면 true 출력
System.out.println(!present);  // isEmpty() 안 쓰고 이렇게 해도 됨

 

 

 

  • Optional에 있는 값 가져오기
    • get() 
    • 만약에 비어있는(null) Optional에서 꺼낸다면?? 런타임 예외 계열인 NoSuchElementException 발생
    • 사실 그냥 get()보다 다른 것들을 사용하는 게 좋다.

 

출력:

 

 

 

  • ifPresent(Consumer)
    • Optional에 값이 있으면 그 값으로 ~을 해라.
      예) Spring으로 시작하는 수업이 있으면 id를 출력하라.

출력:

spring boot

 

 

 

  • orElse(T)
    • 값이 있으면 가져오는데, 있든 없든 T타입의 other 는 그대로 리턴한다.
    • 옵셔널이 감싸고 있는 타입의 인스턴스를 넘겨주는 것.

 

 예)

값이 없을 때(jpa)도 있을 때(spring)도 createNewClasss는 실행된다.

값이 없을 때

출력:

creating new online class

New class

 

값이 있을 때

출력:

creating new online class

spring boot

 

 

 

  • orElseGet(Supplier)
    • 값이 있으면 가져오고, 없으면 Supplier의 인터페이스를 통해 그 인터페이스의 결과를 반환한다.

 

예)

람다식 () -> createNewClasss() 대신, 메소드 레퍼런스로 App::creatNewClasss 해도 된다.

값이 있을 때는 이 Supplier을 실행하지 않는다.

값이 있을 때

출력:

spring boot

 

 

 

  • orElseThrow()
    • Optional에 값이 있으면 가져오고, 없으면 에러를 던진다.

 

 

 

  • Optional filter(Predicate) 
    • 값이 있으면 거른다. (없는 경우 아무 일도 일어나지 않음.)
    • 결과는 Optional → filter에 해당이 되면 그 옵셔널 그대로 나오고, 해당이 안 되면 비어 있는 옵셔널이 나온다.

출력:

true

 

 

 

  • Optional map(Function) 

 

출력:

true

 

 

  • Optional flatMap(Function)
    • 꺼내는 게 Optional이면 map으로 하면 두 번 껍질 까느라 복잡하다.
    • Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용하면 편리하다.
    • 스트림의 flatMap과 다르다.

 

map일 때

 

flatMap일 때

 

 

 

 

 


* 백기선 님의 인프런 강의 <더 자바, Java 8>을 듣고 정리한 내용입니다.

강의 정보: https://www.inflearn.com/course/the-java-java8/


참고

https://junseokdev.tistory.com/19
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을 리턴하라