2 분 소요

Optional

Java에서는 Optional 클래스를 사용해 NPE를 방지할 수 있도록 도와준다.

Optional는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 도와준다.

Optional 활용하기

  • Optional 생성

    public static void main(String[] args) {
        Optional<Integer> integer = Optional.of(10);
        Optional<Integer> integerOptional = 
    						Optional.ofNullable((Math.random() * 10) > 5 ? null : 1);
        Optional<Object> empty = Optional.empty();
    }
    
    • Optional.of(10)

      값이 10인 Optional 객체를 만듭니다. 안의 객체로 들어갈 값은 null을 고려하지 않습니다.만일, null 값이 들어간다면 NullPointException이 발생한다

    • Optional.ofNullable((Math.random() * 10) > 5 ? null : 1);

      0~9까지의 실수가 5보다 클 경우 null을 아닐경우 1을 인자값으로 Optional을 만듭니다. 이처럼 값이 null일수도 있을 경우 o

      f는 에러를 발생하기 때문에 ofNullable()을 사용한다.

    • Optional.empty()

      내부값이 비어있는 Optional을 반환합니다. 주로 반환값이 null일 때 사용한다.

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

    • .isPresent()

      포장된 값이 null 이 아니면 True 를 반납한다.

    • .isEmpty()

      isEmpty() 로 null 인 경우 true 를 얻을 수 있다.

  • Optional에 있는 값 가져오기

public static void main(String[] args) {
    Optional<Integer> integer = Optional.ofNullable(10);
    Optional<Object> empty = Optional.empty();

    System.out.println(integer.get());
    integer.ifPresent(System.out::println);
    integer.orElse(testInteger()); 
    // 포장값이 Null 이 아닌 경우에도 default 객체를 생성한다.  "create integer test" 수행
    integer.orElseGet(App::testInteger); 
    // 포장값이 Null 이 아닌 경우 호출되지 않는다. 수행X
    integer.orElseThrow(NoSuchElementException::new);
}

public static Integer testInteger(){
    System.out.println("create integer test");
    return (int)(Math.random() * 10);
}
  • Get()

  • 내부가 null인 경우를 고려할경우

    • ifPresent :Optional에 값이 있는 경우 그 값을 Consumer Functional Interface에 전달 후 로직을 수행.

    • orElse: 이 있는 경우 가져오고 없는 경우 인자값으로 선언한 내용을 반환한다.

    • orElseGet : 값이 있으면 가져오고 없는 경우에 Supplier Functional Interface로직을 수행한다.

    • orElseThrow : 값이 있으면 가져오고 없으면 에러를 던진다.

  • 내부 값을 걸러내서 가져오기

    • Optional.filter(Predicate) : Predicate Functional Interface를 수행 하여 조건에 부합하는 값을 가져온다.
  • 내부값 변환하기

    • Optional map(Function) : Function Functional Interface를 수행하여 내부 값을 순회하며 변경한 후 반환한다.

    • Optional flatMap(Function) :Optional 안에 들어있는 인스턴스가 Optional인 경우 내부 원소값을 꺼낼 때 사용한다.

Optional 사용 가이드

[ Optional이 위험한 이유, Optional을 올바르게 사용해야 하는 이유 ]

Optional을 사용하면 코드가 Null-Safe해지고, 가독성이 좋아지며 애플리케이션이 안정적이 된다는 등과 같은 얘기들을 많이 접할 수 있

다. 하지만 이는 Optional을 목적에 맞게 올바르게 사용했을 때의 이야기이고, Optional을 남발하는 코드는 오히려 다음과 같은 부작용

(Side-Effect)를 유발할 수 있다.

  • NullPointerException 대신 NoSuchElementException가 발생함

  • 이전에는 없었던 새로운 문제들이 발생함

  • 코드의 가독성을 떨어뜨림

  • 시간적, 공간적 비용(또는 오버헤드)이 증가함

[ 올바른 Optional 사용법 가이드 ]

  • Optional 변수에 Null을 할당하지 말아라

    empty()를 사용 값을 초기화하여 사용하는것을 권장

  • 값이 없을 때 Optional.orElseX()로 기본 값을 반환하라

  • 단순히 값을 얻으려는 목적으로만 Optional을 사용하지 마라

    // AVOID
    public String findUserName(long id) {
        String name = ... ;
          
        return Optional.ofNullable(name).orElse("Default");
    }
      
    // PREFER
    public String findUserName(long id) {
        String name = ... ;
          
        return name == null 
          ? "Default" 
          : name;
    }
    
  • 생성자, 수정자, 메소드 파라미터 등으로 Optional을 넘기지 마라

    // AVOID
    public class User {
      
        private final String name;
        private final Optional<String> postcode;
      
        public Customer(String name, Optional<String> postcode) {
            this.name = Objects.requireNonNull(name, () -> "Cannot be null");
            this.postcode = postcode;
        }
      
        public Optional<String> getName() {
            return Optional.ofNullable(name);
        }
          
        public Optional<String> getPostcode() {
            return postcode;
        }
    }
    

    Optional은 반환 타입으로 대체 동작을 사용하기 위해 고안된 것임을 명심

  • Collection의 경우 Optional이 아닌 빈 Collection을 사용하라

    // AVOID
    public Optional<List<User>> getUserList() {
        List<User> userList = ...; // null이 올 수 있음
      
        return Optional.ofNullable(items);
    }
      
    // PREFER
    public List<User> getUserList() {
        List<User> userList = ...; // null이 올 수 있음
      
        return items == null 
          ? Collections.emptyList() 
          : userList;
    }
    
  • 반환 타입으로만 사용하라

    Optional은 반환 타입으로써 에러가 발생할 수 있는 경우에 결과 없음을 명확히 드러내기 위해 만들어졌으며, Stream API와 결합되

    어 유연한 체이닝 api를 만들기 위해 탄생한 것이다

참고

https://mangkyu.tistory.com/70

https://catsbi.oopy.io/81ee5bdc-6825-46f8-b1d2-c608ab2d6465#e7dc4d5a-6af4-4f3b-9e4c-16794c2f0514

태그:

카테고리:

업데이트: