Optional

이연중·2021년 2월 1일
0

JAVA

목록 보기
11/20

Optional 소개

NullPointerException의 원인

  • null을 리턴, null 체크 안함

메소드에서 작업 중 특별한 상황에서 값을 제대로 리턴 할 수 없는 경우 선택할 수 있는 방법

  • 예외를 던짐(스택 트레이스를 찍기에 비쌈) - 필요할 때가 아닌, 로직을 처리할 때 에러를 처리하는 것은 좋은 습관이 아님

  • null 리턴(비용 문제는 없지만 그 코드를 사용하는 클라이언트 코드 주의)

  • 자바 8부터 Optional을 리턴함(클라이언트에게 코드가 명시적으로 빈 값일 수도 있다는 것을 알리고, 빈 값인 경우에 대한 처리를 강제)

    • public Optional<Progress> getProgress(){
      	return Optional.ofNullable(progress) //값이 하나 있거나 null이거나 .of는 null이 아닌 것을 다룸(null값 있을시 NullPointerExeption)  
      }

Optional

  • 오직 값 한 개가 들어있을 수도 없을 수도 있는 컨테이너

주의

  • 리턴값으로 쓰기를 권장(메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말 것)
    • 매개변수로 쓸 경우: 체크(null 체크, 값이 존재하는지 체크)를 해야할 상황이 많아짐
    • 맵의 키 타입으로 쓸 경우: 맵의 특징을 깨버림("key는 null일 수 없다"라는 특징)
    • 인스턴스 필드 타입: 도메인 클래스 설계의 문제를 유발
  • Optional을 리턴하는 메소드에서 null 리턴하지 말 것(Optional을 쓰는 의미가 없음)
  • Primitive 타입용 Optional은 따로 있음(OptionalInt, OptionalLong....)
  • 다른 컨테이너 성격의 인스턴스(Collection, Map, Stream Array, Optional)들을 Optional로 또 다시 감싸면 안됨

Optional API

Optional 만들기

  • Optional.of()
  • Optional.ofNullable()
  • Optional.empty()

Optional 안에 값 유무 확인

  • isPresent()

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> spring=springClasses.stream()
      					.filter(oc->oc.getTitle().startsWith("spring"))
      					.findFirst();
      boolean present=spring.isPresent();
      System.out.println(present); //true 출력
  • isEmpty() (Java 11부터 제공)

Optional에 있는 값 가져오기

  • get()

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
      					.filter(oc->oc.getTitle().startsWith("spring"))
      					.findFirst();
      
      OnlineClass onlineClass=optional.get();
      System.out.println(onlineClass.getTitle());
  • 만약 비어있는 Optional에서 무엇인가를 꺼낸다면? 런타임Exception(NoSuchElementException) -> 사전 확인 필요

Optional에 값이 있는 경우 그 값을 가지고 ~~를 하기

  • ifPresent(Consumer)

  • ex) Spring으로 시작하는 수업이 있으면 id를 출력할 것

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
      					.filter(oc->oc.getTitle().startsWith("spring"))
      					.findFirst();
      
      optional.ifPresent(oc-> System.out.println(oc.getId()));

Optional에 값이 있으면 가져오고 없는 경우 ~~를 리턴

  • orElse()

  • ex) JPA로 시작하는 수업이 없다면 비어있는 수업을 리턴

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
                      			.filter(oc->oc.getTitle().startsWith("spring"))
                      			.findFirst();
      
      OnlineClass onlineClass=optional.orElse(createNewJpaClass())
      System.out.println(onlineClass.getTitle()); // 값이 있을 경우 해당 값의 Title 출력, 없을 경우 new class의 Title 출력
      ----------------------------------------------------------------------------------------------------
      private static OnlineClass createNewJpaClass() {
         System.out.println("creating new online class");
         return new OnlineClass(10,"New Class",false);
      }
  • 근데 위 코드의 경우에는 무조건 "creating new online class"를 출력한다. 그렇게 하지 않기 위해서는? orElseGet 사용!

  • 이미 만들어져 있는(상수) 것을 사용할 때는 orElse() 사용

Optional에 값이 있으면 가져오고 없는 경우 ~~하기

  • orElseGet(Supplier)

  • ex) JPA로 시작하는 수업이 없다면 새로 만들어서 리턴

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
                      			.filter(oc->oc.getTitle().startsWith("jpa"))
                      			.findFirst();
      
      OnlineClass onlineClass=optional.orElseGet(App::createNewJpaClass);
      System.out.println(onlineClass.getTitle());
         
      private static OnlineClass createNewJpaClass() {
      	System.out.println("creating new online class");
      	return new OnlineClass(10,"New Class",false);
      }
  • 동적으로 작업을 통해 만들어낼 때는 orElseGet 사용

Optional에 값이 있으면 가져오고 없으면 에러 던지기

  • orElseThrow()

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
                      						.filter(oc->oc.getTitle().startsWith("jpa"))
                      						.findFirst();
      
      OnlineClass onlineClass=optional.orElseThrow(IllegalArgumentException::new);
      System.out.println(onlineClass.getTitle());
          
      private static OnlineClass createNewJpaClass() {
           System.out.println("creating new online class");
           return new OnlineClass(10,"New Class",false);
      }

Optional에 들어있는 값 걸러내기

  • Optional filter(Predicate)

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
                      			.filter(oc->oc.getTitle().startsWith("jpa"))
                      			.findFirst();
      
      Optional<OnlineClass> onlineClass=optional
                     					.filter(oc->oc.getId()>10);
      System.out.println(onlineClass.isEmpty()); //true 출력!

Optional에 들어있는 값 변환

  • Optional map(Function)

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
                      			.filter(oc->oc.getTitle().startsWith("jpa"))
                      			.findFirst();
      
      Optional<Integer> integer=optional.map(OnlineClass::getId);
      System.out.println(integer.isPresent());
  • Optional flatMap(Function): Optional 안에 들어있는 인스턴스가 Optional인 경우 사용하면 편리

    • List<OnlineClass> springClasses = new ArrayList<>();
      springClasses.add(new OnlineClass(1, "spring boot", true));
      springClasses.add(new OnlineClass(1, "rest api development", false));
      
      Optional<OnlineClass> optional=springClasses.stream()
                      						.filter(oc->oc.getTitle().startsWith("jpa"))
                      						.findFirst();
      
      Optional<Progress> progress=optional.flatMap(OnlineClass::getProgress);

참고

https://www.inflearn.com/course/the-java-java8

profile
Always's Archives

0개의 댓글