Optional과 NullPointerException

nahy·2024년 7월 26일

Spring

목록 보기
5/6

NullPointerException

NullPointerException은 null을 참조하는 레퍼런스 변수로 메서드를 호출하는 등의 객체 코드를 수행하려는 경우 생기는 예외이다.

Optional

Optional은 return null을 대체할 수 있는 클래스이다.

returnNullOrNotNull() 메서드가 있다. 해당 메서드는 null 혹은 참조할 수 있는 주소값을 반환한다고 하자.

Object A = returnNullOrNotNull();

if (A == null) {
	//null일 경우 수행할 로직
}

이런 식으로 if문을 사용해 A가 null을 참조하고 있는 지 검사할 수도 있지만, 프로그램의 안정성을 위해 Optional을 사용하는 것이 좋다.

왜 NPE(NullPointerException)을 피하는 데 Optional을 써야 할까?

사실 아까 봤던 코드로도 충분히 NPE를 피할 수 있다.
그러나 Optional을 사용했을 때 생기는 2가지 이점이 있다.

  1. NullPointerException을 방지
  2. 코드의 가독성을 높이고, 프로그램의 안정성을 향상

null일 수도 있는 value를 한 겹 감싸기 때문에 null에 대해 메서드를 사용했을 때 터지는 NullPointerException을 1차적으로 방지할 수 있다.

자바에서는 int라는 원시 타입을 감싸는 Integer라는 래퍼 클래스를 제공하고 있다.
Optional은 null일 수도 있는 value를 감싼 객체이므로 (자바 래퍼 클래스처럼) 여러 유용한 인터페이스를 제공하고 있다.
이로 인해 코드의 가독성이 높아지고, 프로그램의 안정성이 향상된다.

Optional의 주요 메서드들

  • get() (사용이 지양됨)
    • 비어있는 Optional 객체에 대해서, NoSuchElementException을 던짐
      ⇒ 이렇게 쓸거면.. 왜 return null 안 쓰고 굳이 Optional 쓰니?
    • 비어있지 않은 Optional에 사용해야 함
  • isPresent() (사용이 지양됨)
    • 안에 값이 있나 없나?를 boolean 값으로 확인 가능
    • Optional.isPresent() 의 경우 객체가 존재하면 true가 리턴되고, 비어있는 경우 false를 리턴 함
  • ifPresent()
    • Optional.ifPresent(Cosumer<? super String> consumer>)
    • Optional에 객체가 존재하면 ( ) 가로 안에 들어가는 작업을 진행하라는 메소드
    • 리턴되는 값 void

[안에 값이 없을 때]

  • orElse(기존에 있는 객체)

    • 안에 값이 없을 때, 기존에 있던 객체를 넣어줌 (대입해줌)
    • Optional이 비어 있다면 파라미터로 입력한 인자를 리턴하게 됨
  • orElseGet((Supplier<? extends T> other))

    • orElseGet(새로운 객체를 생성해달라고 요청하는 함수)
      ⇒ 안에 값이 없을 때, 디폴트 값으로 새로운 객체를 대입

    • 비어있는 Optional 객체에 대해서, 넘어온 함수형 인자를 통해 생성된 객체를 반환

    • orElse(T other) 메소드와 동일하게 리턴하지만 
      비어있는 경우만 함수를 호출해서 성능상 이점 기대 가능

  • orElseThrow(Supplier<? extends X> exceptionSupplier)

    • 비어 있는 Optional객체에 대해, 넘어온 함수형 인자를 통해 생성된 예외를 던짐

    • orElseThrow(새로운 예외 객체를 생성해달라고 요청하는 함수)
      ⇒ 안에 값이 없을 때, 이 예외 객체로 처리해줘 ~

get(), isPresent()를 왜 만들어놓고 지양하라고 하는걸까?

null을 그냥 없는 상태 를 나타내는 목적으로 return null을 쓰는 개발자가 있을수도 있다.

Object A = returnNullOrNotNull();

Optional<String> optStr = Optional.ofNullable(A);
if (optStr.isPresent()) { 
	System.out.println(optStr.get()); 
}

이 코드는 사실

Object A = returnNullOrNotNull();

if (A == null) {
	//null일 경우 수행할 로직
}

와 다를 게 없는 코드이다.

null일 수도 있는 value를 직접적으로 꺼내는 get 메서드와
null을 체크하는 isPresent의 사용은
→ 사실상 if(A == null) 사용이랑 똑같다.

실무에서는 orElse메서드를 쓰고, 예외를 throw하는 방식으로 많이 쓴다고 한다.

[Optional 속 orElse 메서드의 3가지 종류]
만약 안에 든 값이 없을 경우(null일 경우)

  • 기존에 있는 객체를 쓴다 → orElse(기존에 있는 객체)
  • 새로운 객체 생성해서 넣을 것이다 → orElseGet(새로운 객체를 생성해달라고 요청하는 함수)
  • 새로운 예외 객체를 생성해서 던질 것이다 → orElseThrow(새로운 '예외' 객체를 생성해달라고 요청하는 함수)

Optional은 직렬화, 역직렬화가 까다로우므로 필드에는 자주 사용하지 않는다.

+) Supplier 클래스

orElseGet, orElseThrow의 소스코드를 보면 인자로 Supplier 클래스를 받고 있다.

package java.util.function;

/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

orElseGet, orElseThrow 메서드에는 파라미터로
null인 경우 새로운 객체를 만들어주거나, 예외를 던지는 함수가 들어갈 수 있다.

마치 JS의 배열 고차함수 중 map(), filter() 등이 인자로 콜백함수를 받는 것과 비슷하다고 느꼈다.

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
	if (value != null) {
		return value;
	} else {
		throw exceptionSupplier.get(); //이 부분에서 사용
	}
}
public T orElseGet(Supplier<? extends T> supplier) {
	return value != null ? value : supplier.get();
}

실제로 소스코드를 보면 supplier의 결과를 꺼내서 리턴하거나 throw 하는 것을 볼 수 있다.

또한 value != null같은 방식으로 null 체크를 해 주는 것도 확인할 수 있다.
Optional의 메서드들 안에서 우리 대신 값의 null 체크를 해 주므로 아까 말했던 isPresent(), get()같은 애들 써서 굳이 한번 더 null 체크로 고생하지 말고 orElseGet, orElseThrow를 사용하여 조금 더 효율적으로 Optional 클래스를 사용해 보도록 하자!

profile
ggg...

2개의 댓글

comment-user-thumbnail
2024년 7월 26일

오 Optional 의 메소드를 어떻게 사용하는데 그치지 않고 왜 Optional을 사용하는지도 이해가 쏙쏙 되네요. Supplier에 대해서도 자세히 알 수 있는 유익한 글입니다! ㅎㅎ

답글 달기
comment-user-thumbnail
2024년 7월 26일

Optional 클래스는 이래서 쓰는 거군요!
좋은 글 감사합니다 :)

답글 달기