본문 바로가기
프로그래밍/JAVA 내용정리

[JAVA] orElse와 orElseGet의 차이

by 노잼인간이라불립니다 2023. 6. 8.

0.  개요

 최근 요구사항이 새로 들어오면서 코드를 수정해야할 일이 생겼다. 로직을 작성하던 중에 Stream을 사용했다. 그러나 코드를 작성하고 나서 실행 시켰을 때 NullPointException이 발생하는 현상이 발생했다.

 

 스트림을 사용하면서 사용한 메서드들은 filter(), findFirst(), orElseGet() 이렇게 3가지를 사용했다.

1.  문제 정의

 디버깅을 하면서 살펴본 결과 orElseGet()이 실행되면서 NullPointException이 발생했다. 원인을 짐작해보건데 내가 코드를 작성하면서 orElse와 orElseGet에 대한 이해도가 높지 않아 ()안에 null을 넣으면서 발생한 이슈라고 판단된다.

 

2.  각 메서드 개념 정리

 해결방법은 간단하다 orElse와 orElseGet 그리고 orElseThrow에 대한 이해를 확실히 하고, 사용하는 것이다. 그러기 위해서 이번 기회에 orElse와 orElseGet과 orElseThrow의 차이점에 대해서 확실히 이해하고 넘어가려 한다.

 

 1) orElse

orElse에 대한 설명

 먼저 메서드를 설명해놓은 초록색 부분를 살펴보자. 하나하나 해석해보면,

 " value가 존재하면 value를 반환하고, 그렇지 않으면 other를 반환한다. "

 " 파라미터: other - value가 존재하지 않으면 반환 될 값. null이 될 수 있습니다. "

 " 반환: value가 존재하면 value리턴 그렇지 않으면 other 리턴. "

 정리해보자면, "value값이 존재하면 value를 반환하고, value 값이 존재하지 않는다면 other를 리턴한다. other는 null이 가능하다." 라고 정리할 수 있겠습니다. 

 

 2) orElseGet

orElseGet에 대한 설명

 orElseGet도 마찬가지로 초록색 부분를 살펴보자!! 하나하나 해석해보면,

" value가 존재하면 value를 리턴한다. 그렇지 않으면 supplying 함수에서 반환된 결과값을 반환한다. "

" 파라미터: supplier - supplying function이 생산해주는 value가 리턴된다. "

" 반환: value가 존재하면 value가 리턴 그렇지 않으면 supplying function에 의해 결과가 생산된다. "

" Throws: NullPointException - 만약에 value가 없으면서 supplying function이 null일경우 발생 " - 이 부분이 내가 경험한 NullPointException이다.

 정리하기 전 잠시 orElse와 비교해가면서 살펴보면, orElse와 굉장히 흡사하다. 다만 차이점이 있다면, orElse는 만약에 value값이 없다면, other이라는 Nullable한 변수를 리턴해주는 것이었다면, orElseGet은 supply function을 실행시켜서 생산되는 값을 리턴해준다. 그리고 이때 supplyfunction은 null이 될 수 없다. 무조건 생산된 값을 리턴해주는 supply function을 넣어주어야 한다.

 즉, orElseGet에서의 핵심은 이것이다. supply function으로부터 생산된 값이 null이어도 상관은 없다. 다만 supply function 자체는 null이 되면 안된다. 위와 같이 NullPointException이 발생한다.

  

 

3) orElseThrow

 

orElseThrow도 초록색 부분을 살펴보자!!

" 만약 value가 존재하면 value를 반환하고 그렇지 않으면 NoSuchElementException을 발생시킨다. "

" 반환: optional에 의해 설명되는 null이 아닌 값. "

" Throws: NoSuchElementException - value가 존재하지 않으면 발생. "

 described by가 조금 해석하기 애매? 했지만 일단 전반적으로 정리해보자면, value가 null값이라면 NoSuchElementException을 발생시키고, null이 아닐 경우에는 value를 반환한다.

 

3.  정리

3가지 메서드에 대해서 설명과 코드를 살펴보았다. 아직 까지 스트림을 통해 많은 코딩을 해보지 않아서 어떤 메서드는 언제 쓸지 고민을 해보아야 겠지만, 대략적으로 정리해보자면 다음과 같다.

 

1. 일단 3가지 메서드 모두 앞의 메서드의 반환 값이 optional일때 사용 가능하다. 즉, 앞의 메서드로부터 null값이 넘어올 수도 있다는 뜻! 그러니 null 처리를 어떻게 할 것인가에 대한 처리가 필요할 때 3가지 메서드를 고민해 보아야 할 것이다!

 

2. null 처리를 어떻게 할 것인가? 3가지 옵션이 있다.

    2-1. orElse: null 처리를 단일 값 리턴으로 하고 싶을 때 사용한다. (값이 null일 경우 별 다른 실행 코드 필요 없이 default값을 리턴하려고 할 때 사용하면 유용할 것 같다!)

 

    2-2. orElseGet: null 처리를 실행코드를 실행시켜서 새로운 값을 생성하여 리턴 으로 처리하고 싶을 때 사용한다.(값이 null일 경우 실행코드를 실행시켜 값을 만들어 리턴받아 사용하고 싶을 때 유용할 것 같다!)

 

    2-3. orElseThrow: null 값이 발생했을 경우 Exception을 발생 시키고 싶을 때 사용한다. (Exception을 발생시켜 try catch를 통해 다른 로직을 실행시키거나, 아니면 아예 Transaction rollback 되도록 할 때 유용할 듯 하다!)

 

 

4.  해결방법

위와 같은 3가지 메서드를 사용하지 않고 다른방법으로 우회하여 해결했다.

1. 일단 내가 실행시키는 배치 잡의 로직은 Exception이 발생하지 않아야한다는 전제조건이 있었다.

2. 그리고 현재 스트림의 로직은 processor단에서 쓰여진 로직이다.

위 두 가지 조건을 기반으로 findFirst가 아닌 toList()로 스트림의 결과 값을 리턴하게 하는 방법으로 진행하였다. 그래서 리턴받은 List의 크기가 0일 경우 null을 리턴하여 processor단에서 걸러지도록 하였고, 그 이외의 경우에는 코드를 정상적으로 진행하게 끔 로직을 작성하였다.

 

5.  마지막으로

 사실 더 좋은 해결방법이 있을수도 있고, 위 3가지를 정확히 학습하여 로직을 작성했으면 좋았겠지만, 나에게 주어진 개발시간은 그리 녹록치 않았기에 위의 우회적인 해결방법으로 코드를 작성하였다. 그리고 Best Practice를 좀 더 고민하기 위해서 이 글을 작성하였다.

 위의 해결방법도 좋지만, 3가지 메서드에 대해서 학습하면서 orElse를 이용하여 null 인지 여부를 판단하는 것도 기존의 코드수정을 최대한 줄이면서 할 수 있었던 더 좋은 방법이었다고 생각한다. (물론 더 좋은 방법이 있을 수도 있다..)

 그러나 코드 작성과 수정 당시의 나는 3가지 메서드의 차이점에 대해서 어렴풋이 알고 있었고, 그 결과는 오류로 이어졌다. 역시 다시 한번 깨닫는 것이지만, 코드를 한 줄 쓰더라도 제대로 알고 쓰는 것과 어렴풋이 알고 쓰는 것은 천지차이라는 것을 느끼면서 다시한번 자기 반성을 해본다.

 앞으로는 시간에 쫓기더라도 한번 이라도 더 찾아보고 개념을 정립한 후 코드를 작성하고자 노력할 것이다. (시간에 쫓기니까 허술해진다.. 후..)

 

끝.