ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java 8 - 4. Optional
    공부일기/Java 8 2020. 12. 22. 15:32

    1. Optional이란?

    비어있을 수도 있고, 뭔가를 담고 있을 수도 있는 컨테이너 인스턴스의 타입!

    자바 프로그래밍에서 NullPointerException을 종종 보게 되는 이유

    • null을 리턴하니까! && null 체크를 깜빡했으니까!

     

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

    • 예외를 던진다. (비싸다, 스택트레이스를 찍어두므로 예외를 던지는거 자체가 리소스에 영향을 줌.)

    • null을 리턴한다. (비용 문제가 없지만 그 코드를 사용하는 클라이언트 코드가 주의해야 한다.)

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

     

    주의할 것

    • 리턴값으로만 쓰기를 권장한다. (메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말자.)  

    • Optional을 리턴하는 메소드에서 null을 리턴하지 말자.

    • 프리미티브 타입용 Optional을 따로 있다. OptionalInt, OptionalLong,...

    • Collection, Map, Stream Array, Optional은 Opiontal로 감싸지 말 것. 

     

     

     

    2. Optional API

    Optional 만들기

    • Optional.of()

    • Optional.ofNullable()

    • Optional.empty()

     

    Optional에 값이 있는지 없는지 확인하기

    • isPresent()

    package optional;
            
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        public static void main(String[] args) {
            List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
    
            Optional<Integer> optional = numbers.stream()
                    .filter(number -> number.equals(5))
                    .findFirst();
    
            if (optional.isPresent()){
                Integer five = optional.get();
                System.out.println(five);
            }
        }
    }
    5

     

     

    • isEmpty() (Java 11부터 제공)

    package optional;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        public static void main(String[] args) {
            List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
            
            Optional<Integer> emptyOptional = numbers.stream()
                    .filter(number -> number.equals(13))
                    .findFirst();
    
            if (emptyOptional.isEmpty()) {
                System.out.println("nothing");
            }
        }
    }
    nothing

     

     

     

    Optional에 있는 값 가져오기

    • get()

    package optional;
            
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        public static void main(String[] args) {
            List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
    
            Optional<Integer> optional = numbers.stream()
                    .filter(number -> number.equals(5))
                    .findFirst();
    
            if (optional.isPresent()){
                Integer five = optional.get();
                System.out.println(five);
            }
        }
    }

    위에서 사용한 예제와 동일하다.

     

     

    • 만약에 비어있는 Optional에서 무언가를 꺼낸다면??

    인텔리제이에서 get()을 호출하기 전에 값이 존재하는지 확인 후에 꺼낼 것을 권장한다. NoSuchElementException이 발생하기 위해 if문을 이용하거나 아래 나와있는 API를 활용할 수 있다.

     

     

     

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

    • ifPresent(Consumer)

    package optional;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        public static void main(String[] args) {
            List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
    
            Optional<Integer> optional = numbers.stream()
                    .filter(number -> number % 2 == 0)
                    .findFirst();
    
            optional.ifPresent(even -> System.out.println(even));
        }
    }
    10

     

     

    • 예) Spring으로 시작하는 수업이 있으면 id를 출력하라.

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("spring"))
                    .findFirst();
    
            optional.ifPresent(oc -> System.out.println(oc.getId()));
    
        }
    }
    1

     

     

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

    • orElse(T)

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("jpa"))
                    .findFirst();
    
            OnlineClass onlineClass = optional.orElse(createNewClass());
            System.out.println(onlineClass.getTitle());
    
        }
    
        private static OnlineClass createNewClass() {
            return new OnlineClass(3, "New jpa", false);
        }
    }
    New jpa

    위 예제의 경우 startsWith("jpa")가 아닌 startsWith("spring"), 즉 spring 수업이 있는 경우에도 orElse를 실행하고 새로운 수업이 만들어진다. 이런 경우 orElseGet을 이용하면 된다.

    이미 만들어져있는 인스턴스를 참고해서 사용하는 경우 사용하기 적합하다.

     

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

    • orElseGet(Supplier)

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("jpa"))
                    .findFirst();
    
            OnlineClass onlineClass = optional.orElseGet(practice::createNewClass);
            System.out.println(onlineClass.getTitle());
    
        }
    
        private static OnlineClass createNewClass() {
            System.out.println("CREATING NEW ONLINE CLASS");
            return new OnlineClass(3, "NEW CLASS", false);
        }
    }
    CREATING NEW ONLINE CLASS
    NEW CLASS

    동적으로 추가 작업이 필요한 경우 orElseGet을 사용하는게 적절하다.

     

     

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

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("jpa"))
                    .findFirst();
    
            OnlineClass onlineClass = optional.orElseGet(practice::createNewClass);
            System.out.println(onlineClass.getTitle());
    
        }
    
        private static OnlineClass createNewClass() {
            System.out.println("CREATING NEW ONLINE CLASS");
            return new OnlineClass(3, "NEW CLASS", false);
        }
    }
    CREATING NEW ONLINE CLASS
    NEW CLASS

    위 예제와 동일하다. 

     

     

    이미 만들어져있는 인스턴스를 참고해서 

     

    Optional에 값이 있으면 가져오고 없는 경우 에러를 던져라.

    • orElseThrow()

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("jpa"))
                    .findFirst();
    
            OnlineClass onlineClass = optional.orElseThrow(() -> {
                return new IllegalStateException("없습니다.");
            });
            System.out.println(onlineClass.getTitle());
    
        }
    
        private static OnlineClass createNewClass() {
            System.out.println("creating new online class");
            return new OnlineClass(3, "New class", false);
        }
    }

    대체할 게 없는 경우 에러를 던지도록 만들 수 있다.

     

     

     

    Optional에 들어있는 값 걸러내기

    • Optional filter(Predicate)

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("spring"))
                    .findFirst();
    
            Optional<OnlineClass> onlineClass = optional.filter(oc -> oc.isClosed());
    
            System.out.println(onlineClass.isPresent());
        }
    }
    true

    spring으로 시작하는 optional 중에 isClosed()가 true인 값들을 걸러내고, isPresent()를 통해 존재하는 경우 true 출력, 존재하지 않는 경우 false를 출력하는 코드.

    있다는 가정하에 동작하며 없는 경우 아무일도 일어나지 않는다.

     

     

     

    Optional에 들어있는 값 변환하기

    • Optional map(Function)

    package optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class practice {
        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 development", false));
    
            Optional<OnlineClass> optional = springClasses.stream()
                    .filter(oc -> oc.getTitle().startsWith("spring"))
                    .findFirst();
    
            Optional<Integer> integer = optional.map(oc -> oc.getId());
            System.out.println(integer.isPresent());
        }
    }
    true

     

     

    • Optional flatMap(Function): Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용하면 편리하다.

     

    flatMap을 사용하는 경우 return 되는 타입이 optional인 경우 한 번 더 박스에서 꺼내 해당 타입을 반환한다.

     

Designed by Tistory.