ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Stream(스트림) - 4. Optional<T> & OptionalInt
    Java 2020. 3. 10. 21:16
    반응형

    Optional(JDK 1.8~)

     Optional<T> : 제네릭 클래스로 "T타입의 객체"를 감싸는 래퍼 클래스, Optional타입의 객체에는 모든 타입의 참조변수를 담을 수 있음

    public final class Optional<T> {
        private final T value;    // T타입의 참조변수
            ...
    }

    최종 연산의 결과 타입이 Optional인 경우

     최종 연산의 결과를 그냥 반환하는 것이 아닌 Optional 객체에 담아서 반환하는 것

     객체에 담아서 반환하면, 반환한 결과가 null인지 체크하는 대신 Optional에 정의된 메서드를 통해서 간단히 처리할 수 있음

     널 체크를 위한 if문 없이도 NullPointerException이 발생하지 않는 보다 간결하고 안전한 코드를 작성하는 것이 가능해짐

    Object 클래스에 isNull(), nonNull(), requireNonNull()과 같은 메서드가 있는 것도 널 체크를 위한 if문을 메서드 안으로 넣어서 코드의 복잡도를 낮추기 위한 것

    Optional 객체 생성

     of() 또는 ofNullable()을 사용하여 생성함

    String str = "abc";
    Optional<String> optVal1 = Optional.of(str);
    Optional<String> optVal2 = Optional.of("abc");
    Optional<String> optVal3 = Optional.of(new String("abc"));
    Optional<String> optVal4 = Optional.of(null); // NullPointerException 발생
    
    Optional<String> optVal5 = Optional.ofNullable(null); // OK

     기본값으로 초기화시 empty() 사용, null로 초기화할 수 있지만 empty()를 사용하는 것이 바람직함

    - empty()는 제네릭 메서드라 앞에 <T>를 붙이지만, 추정 가능하므로 생략할 수 있음

    Optional<String> optVal1 = null; // 널로 초기화
    Optional<String> optVal2 = Optional.<String>empty(); // 빈 객체로 초기화
    Optional<String> optVal3 = Optional.empty(); // 타입 추정 가능하므로 제네릭 생략 가능

    Optional 객체의 값 가져오기

     get() : Optional 객체의 값을 가져올 때 사용, null일 때는 NoSuchElementException이 발생

     orElse() : get()과 비슷하지만, null일 때 대체할 값을 지정할 수 있음

    Optional<String> optVal = Optional.of("abc");
    String str1 = optVal.get();    // optVal에 저장된 값을 반환, null이면 예외 발생
    String str2 = optVal.orElse(); // optVal에 저장된 값을 반환, null이면 "" 반환

     orElseGet() : null을 대체할 값을 반환하는 람다식을 지정할 수 있음 (orElse()의 변형)

     orElseThrow() : null일 때 지정한 예외를 발생시킴(orElse()의 변형)

    T orElseGet(Supplier<? extends T> other)
    T orElseThrow(Supplier<? extends X> exceptionSupplier)
    
    String str3 = optVal.orElseGet(String::new); // () -> new String()과 동일
    String str4 = optVal.orElseThrow(NullPointerException::new); // null이면 예외 발생 

     Stream처럼 Optional 객체도 filter(), map(), flatMap() 사용 가능 - Optional 객체 값이 null이라면 아무 일도 하지 않음

     map()의 연산결과가 Optional<Optional<T>>일 때, flatMap()을 사용하면 Optional<T>를 결과로 얻음

    int result = Optional.of("123")
                      .filter(x -> x.length() > 0)
                      .map(Integer::parseInt).orElse(-1); // result = 123
    
    result = Optional.of("")
                     .filter(x -> x.length() > 0)
                     .map(Integer::parseInt).orElse(-1); // result = -1

     parseInt()는 예외가 발생하기 쉬운 메서드

    - 예외처리된 메서드를 만든다면? 

    static int optionalStringToInt(Optional<String> optionalString, int defaultValue) {
        try {
            return optionalString.map(Intger::parseInt).get();
        } catch (Exception e) {
            return defaultValue;
        }
    }

     isPresent() : Optional 객체의 값이 null이면 false, 아니면 true 반환

     ifPresent(Consumer<T> block) : 값이 있으면 주어진 람다식을 실행, 없으면 아무 일도 하지 않음

    /* 조건문 */
    if(str != null) {
        System.out.println(str);
    }
    
    /* isPresent 이용하여 변경 */
    if(Optional.ofNullable(str).isPresent()) {
        System.out.println(str);
    }
    
    /* ifPresent 이용하여 변경 */
    Optional.ofNullable(str).ifPresent(System.out::println);

     Optional<T>를 반환하는 Stream 클래스에 정의된 메서드 - ifPresent()와 잘 어울림

    - max()와 min()은 rduce()를 이용해 작성된 것

    Optional<T> findAny()
    Optional<T> findFirst()
    Optional<T> max(Comparator<? super T> comparator)
    Optional<T> min(Comparator<? super T> comparator)
    Optional<T> reduce(BinaryOperator<T> accumulator)

    OptionalInt & OptionalLong & OptionalDouble

     IntStream과 같은 기본형 스트림은 Optional도 기본형을 값으로 하는 OptionalInt, OptionalLong, OptionalDouble 반환

     IntStream에 정의된 메서드 - 반환 타입이 Optional<T>가 아니라는 것을 제외하고는 Stream에 정의된 것과 비슷함

    OptionalInt    findAny()
    OptionalInt    findFirst()
    OptionalInt    reduce(IntBinaryOperator op)
    OptionalInt    max()
    OptionalInt    min()
    OptionalDouble average()

     기본형 Optional에 저장된 값을 꺼낼 때 사용하는 메서드의 이름이 조금씩 다르다는 것에 주의

    Optional<T>    - T      get()
    OptionalInt    - int    getAsInt()
    OptionalInt    - long   getAsLong()
    OptionalDouble - double getAsDouble()

     OptionalInt 정의

    public final class OptionalInt {
        ...
        private final boolean isPresent; // 값이 저장되어 있으면 true
        private final int value;         // int 타입의 변수
        ...
    }

     기본형 int의 기본값은 0 = 아무런 값도 갖지 않는 OptionalInt에 저장된 값은 0, 0을 저장한 객체와는 같지 않음

    OptionalInt opt1 = OptionalInt.of(0);    // OptionalInt에 0 저장
    OptionalInt opt2 = OptionalInt.empty();  // OptionalInt에 0 저장됨 - 기본값 0
    
    System.out.println(opt1.isPresent()); // true  - 0이 저장됨
    System.out.println(opt2.isPresent()); // false - 값이 비어있음
    
    System.out.println(opt1.getAsInt());  // 0
    System.out.println(opt2.getAsInt());  // NoSuchElementException예외발생
    
    System.out.println(opt1.equals(opt2)); // false
    

     Optional객체의 경우 null 저장시 비어있는 것과 동일

    Optional<String> opt1 = Optional.ofNullable(null);
    Optional<String> opt2 = Optional.empty();
    
    System.out.println(opt.equals(opt2)); // true
    반응형

    댓글

Designed by Tistory.