java.util.Optional이란?
예전에 개인 프로젝트를 하면서 NullPointerException(NPE)를 피하기 위해 사용했던 Optional을 한 번 정리하고 싶어 포스팅을 하게 되었다.
지금부터 Java8부터 지원하는 Optional 클래스에 대해 알아보려고 한다.
1. Optional이란?
NullPointerException(NPE)
개발자들을 가장 많이 괴롭히는 예외 중 하나가 바로 NPE(NullPointerException)이다.
그럼 NPE를 피하기 위해서는 어떤 방법이 있을까?
Test t = null;
System.out.println("t = " + t.v); //NPE 발생
//방어 로직
if(t == null){ //방어 로직
System.out.println("Test is NULL");
}else{
System.out.println("t = " + t.v);
}
NPE를 피하기 위해서는 위와 같이 if..else 문으로 null을 검사하는 방어 로직을 추가해야한다. 하지만 이 방법은 null값을 검사해야하는 변수들이 많아지고 로직이 복잡하면, 코드를 지저분하고 가독성을 떨어뜨린다.
이를 해결하기 위해 나온 것이 Optional 클래스이다.
Optional이란?
oracle docs
A container object which may or may not contain a non-null value. If a value is present, isPresent() returns true. If no value is present, the object is considered empty and isPresent() returns false.
Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (returns a default value if no value is present) and ifPresent() (performs an action if a value is present).
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.
오라클 공식 문서에서는 Optional을 다음과 같이 정의한다.
null인 값을 포함할 수 있는 컨테이너 오브젝트(래퍼 클래스)이다. 만약 null이 들어있으면 isPresent()는 true를 반환하고, 아니면 false를 반환한다. orElse()와 ifPresent()와 같은 포함된 값의 존재 여부를 확인하는 메서드들도 추가적으로 제공한다.
이는 값 기반의 클래스로서, Optional 인스턴스에 식별에 민감한 연산(==, 식별 해시 코드, 동기화 등)을 사용하면 예기치 않은 결과가 발생할 수 있으므로 피해야한다.
2. Optional 사용법
Optional 생성
Optional은 아래와 같이 빈 객체를 생성할 수 있다.
Optional<String> optional = Optional.empty();
isEmpty(), isPresent()
isEmpty()와 isPresent() 메서드를 사용해 null 여부를 판단할 수 있다.
Optional<String> optional = Optional.empty();
System.out.println(optional.isEmpty()); //true
System.out.println(optional.isPresent()); //false
ofNullable()
만약 null이 올 수 도 있는 경우에는 Optional.ofNullable()를 사용해 해당 값을 Optional로 감싸줄 수 있다.
Optional<String> optional = Optional.ofNullable(getString());
orElse(), orElseGet(), orElseThrow()
Optional에서 제공하는 orElse(), orElseGet(), orElseThrow()을 활용해 기존의 NPE 방어 로직보다 간단히 표현할 수 있다.
그리고 해당 메서드들은 null값의 여부에 따라 기존값 또는 정해준 예외값을 리턴해주기 때문에 결과값을 Optional로 감싸줄 필요가 없다.
String result1 = Optional.ofNullable(str).orElse("empty"); // null 값이 들어오면 "empty" 반환
String result2 = Optional.ofNullable(str).orElseGet(() -> "empty"); // null 값이 들어오면 "empty" 반환
String result3 = Optional.ofNullable(str).orElseThrow(NullPointerException::new); //null 값이 들어오면 NPE 처리
orElse와 orElseGet 차이
- orElse : null값 여부와 상관없이 항상 호출된다.
- orElseGet : null일 때만 호출된다
String val = "NonNull";
String result1 = Optional.ofNullable(val).orElse(Test.getTest());
System.out.println("result1 = " + result1);
String result2 = Optional.ofNullable(val).orElseGet(Test::getTest);
System.out.println("result2 = " + result2);
결과
getTest() 호출
result1 = NonNull
result2 = NonNull
출력 결과를 분석해보면 orElse()의 경우 값이 null이든 아니든 뒤의 연산이 진행되어 getTest() 호출이 출력된 것을 볼 수 있다.
반면에 orElseGet()의 경우에는 null일 때만 뒤의 연산이 진행되므로 getTest() 호출이 출력되지 않은 것을 확인할 수 있다.
이 부분을 고려하지 않으면 성능 이슈가 발생할 수도 있으니 주의해서 사용해야한다.
4. Optional 정리
Optional은 null 또는 실제 값을 래퍼로 감싸서 NPE(NullPointerException)로부터 자유로워지기 위해 나온 래퍼 클래스이다.
따라서 Optional을 반환하는 메소드는 절대 null을 반환해서는 안된다.
또한 Optional은 값을 래핑하고 다시 풀고, null일 경우에는 대체하는 함수를 호출하는 등의 오버헤드가 있으므로 성능이 저하될 수 있다.
그렇기 때문에 메소드의 반환 값이 절대 null이 아니라면 Optional을 사용하지 않는 것이 좋다.
5. 참고
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] JVM (0) | 2021.10.25 |
---|---|
자바(Java) 버전별 특징 (0) | 2021.10.19 |
[스터디 할래] 6주차 - 상속 (0) | 2021.05.26 |
[스터디 할래] 5주차 - 클래스 (0) | 2021.05.26 |
int와 Integer의 차이(Wrapper Class) (0) | 2021.05.26 |
댓글