상세 컨텐츠

본문 제목

Optional 사용에 대한 고찰 (사용목적, 메서드, 주의사항)

Spring/JAVA

by Chan.94 2024. 9. 6. 14:06

본문

반응형

Optional의 목적

  • 메서드가 반환할 결과 값이 '없음'을 명백하게 표현한다.
    null을 반환하면 에러가 발생할 가능성이 높은 상황에서 메서드의 반환 타입으로 Optional을 사용하자는 것이 Optional을 만든 주된 목적이다.
  • 코드의 안정성을 향상시키고 코드 가독성이 높아진다.
    호출하는 쪽에서 null이 반환될 가능성이 있음을 명확히 알 수 있어, null 값에 대한 처리를 강제할 수 있다.
    따라서, Null Pointer Exception 발생일 방지할 수 있다.
Java8 이전에는 메서드의 리턴 타입으로 ‘없음’을 표현하기 위한 용도로 null이 사용되었다. 

그러나 객체의 값이 null인 경우에, null 체크 없이 해당 객체의 메서드를 호출하거나 하는 등, 부주의하게 사용하는 경우에 ‘NPE(NullPointerException)’이 발생하는 상황이 발생했다.

이런 상황을 방지하고자 Optional이 만들어지게 되었다. 

Optional 기본 문법

Optional 생성

Optional은 기본 생성자가 private로 직접적으로 생성자를 호출하여 Optional을 생성할 수 없고, Optional에서 제공하는 static 메서드를 사용하여 Optional을 만들어 낼 수 있다.

empty()

Optional<Member> optional = Optional.empty();

값이 비어있는 Optional을 생성한다.

 

of()

Member member = memberRepository.findById(id);
Optional<Member> opt = Optional.of(member);

객체가 null이 아닌 경우 사용한다. 값이 null이면 NullPointerException을 발생한다.

 

ofNullable

Member member = memberRepository.findById(id);
Optional<Member> opt = Optional.ofNullable(member);

객체가 null일 수도 있는경우 사용한다. 값이 null이면 Optional.empty()가 반환된다.


Optional 값 처리

isPresent()

Optional의 값이 존재하면 true, 해당 값이 없으면 false를 반환한다.

 

isEmpty()

Optional의 값이 없으면 true, 해당 값이 존재하면 false반환한다.

 

get()

Optional<String> optional = Optional.of("Hello");

if (optional.isPresent()) {
    System.out.println("Optional value : " + optional.get());
} else {
    System.out.println("Optional is empty");
}

값을 얻기 위해서 사용하며, 만일 Optional의 값이 비어있는 경우, NoSuchElementException이 발생한다.

값이 항상 있다고 판단되지 않는다면 isPresent() 또는 isEmpty()로 값의 존재 여부를 체크한 뒤, get() 메서드를 사용해야 한다.

 

filter()

Optional<String> opt = Optional.of("Hello");
opt.filter(data -> data.length() >= 3)
   .ifPresent(data -> System.out.println("value: " + data));

Optional의 값에 filter를 적용하여 조건에 맞는 값인 경우에 사용할 경우

 

map()

Optional<String> opt = Optional.of("Hello");
opt.filter(data -> data.toUpperCase())
   .ifPresent(data -> System.out.println("filter false value: " + data));

Optional의 값을 변환해서 사용해야 하는 경우


ifPresent()

// TEST 출력
Optional.of("TEST").ifPresent(System.out::println);

// 아무것도 출력되지 않음
Optional.ofNullable(null).ifPresent(System.out::println);

값이 존재할 경우 특정 로직을 수행한다.

 

ifPresentOrElse()

Optional<String> optionalString = Optional.of("Hello");

optionalString.ifPresentOrElse( val -> System.out.println("Optional value : " + val)
                               , () -> System.out.println("Optional is empty") );

ifPresent()와 유사하다. 첫 번째 람다식은 값이 있는 경우 실행되고 두 번째 람다식은 값이 없는 경우 실행된다.

 

orElse()

String static DEFAULT_VALUE = "DEV_LOG";
Optional<String> empty = Optional.empty();
String result = empty.orElse(DEFAULT_VALUE);

값이 존재하면 그 값을 반환하고, 값이 없으면 주어진 다른 값을 반환한다.

orElse 메서드의 인자는 Optional 객체가 존재할 때도 실행된다.

따라서 orElse()는 새 객체 생성이나 새로운 연산을 유발하지 않고 이미 생성되었거나 계산된 값일 때만 사용해야 한다.

다른 경우라면 orElseGet()을 사용하자.


orElseGet()  

Optional<Member> empty = Optional.empty();
Member member = empty.orElseGet(() -> new Member());

값이 존재하면 그 값을 반환하고, 값이 없으면 주어진 supplier 함수를 사용하여 값을 생성한다.

Optional에 값이 없을 때만 새 객체를 생성하거나 새로운 연산을 수행하므로 불필요한 오버헤드가 없다.

 

orElseThrow()

Optional<String> empty = Optional.empty();
String return = empty.orElseThrow(() -> new RuntimeException("exception"));

값이 존재하면 그 값을 반환하고, 값이 없으면 예외를 발생시킨다.


Optional 사용 시 주의사항

1) Optional 변수에 null을 할당하지 않는다.

반환 값으로 null을 사용하는 것이 위험하기 때문에 등장한 것이 Optional 이다.당연히 Optional 대신 null 을 반환하는 것은 Optional의 도입 의도와 맞지 않는다.

 

Optional은 null을 사용하지 않기 위해서 만들어진 클래스인데, null로 초기화하면 Optional의 사용 의도와 맞지 않다.

"결과 없음"을 표현해야 하는 경우라면 null 대신 Optional.empty()를 반환하면 된다.

Optional<Member> findById(Long id) {
    //DB
    List<Member> list = memberRepository.findById(id);
    
    if (list == null) {        
        return Optional.empty();    
    }
}

 

2) Optional을 필드 타입으로 사용하지 않는다.

Optional은 메서드의 리턴 타입으로 사용하기 위한 용도로 설계되었기 때문에, Optional을 클래스의 필드로 선언하는 것은 잘못된 사용 방식이다.

 

3) 메서드의 인자로 Optional을 사용하지 않는다.

Optional은 메서드의 리턴 타입으로 사용하기 위한 용도로 설계되었기 때문에, Optional을 메서드의 인자로 사용하는 것도 잘못된 사용 방식이다.

 

4) Optional을 빈 컬렉션이나 배열을 반환하는 데 사용하지 않는다.

컬렉션이나 배열로 복수의 결과를 반환하는 메서드가 "결과 없음"을 가장 명확하게 나타내는 방법은 대부분의 경우 빈(empty) 컬렉션 또는 배열을 반환하는 방법이다.

컬렉션이나 배열이 ‘비어있음’을 나타내기 위해서 굳이 Optional을 사용하는 것은 적합한 사용 방식이 아닙니다.

 

5) Optional.get() 호출 전에 Optional 객체가 값을 가지고 있음을 확실히 한다.

Optional 내부 값이 존재하지 않을 경우 NoSuchElementException을 발생하기 때문에 값을 가져오기 전에 반드시 값이 있는지 확인해야 한다.

 

6) 값이 없는 경우, Optional.orElse()를 통해 이미 생성된 기본 값(객체)을 반환해야 한다.

orElse 메서드의 인자는 Optional 객체가 존재할 때도 실행된다.
따라서, orElse()는 새 객체 생성이나 새로운 연산을 유발하지 않고 이미 생성되었거나 계산된 값일 때만 사용해야 한다.
다른 경우라면 orElseGet()을 사용하자.

 

7) Optional은 성능이 중요한 코드에 신중하게 사용한다.

Optional은 추가적인 객체를 생성하는 오버헤드가 있습니다. 성능이 중요한 루프 내에서 자주 사용하면 성능 저하를 초래할 수 있습니다. 성능이 중요한 상황에서는 가볍게 사용하는 것이 중요합니다.

반응형

관련글 더보기

댓글 영역

>