ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Optional
    프로그래밍/Java 2022. 5. 5. 09:22

    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);
      }
    }

     

     

     

    참고

    '프로그래밍 > 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
Designed by Tistory.