-
[JAVA8] About Optional-1 / Optional에 대하여-1Backend/JAVA&JAVA8 2022. 5. 5. 13:46
안녕하세요.오늘은 Java8에서 빠질 수 없는 개념, Null 을 다루는 새로운 방법인 Optional에 대해서 다루어 볼까 해요.
Optional은 Java 진영에서 꺼낸 비장의 무기 같은 느낌이 듭니다.
Null에 대한 지속적인 불만을 토론한 개발자들의 의견이 겨우 반영된 느낌이랄까요?
정작 저는 Java8이 나왔을 때 당시에 제대로 사용하기는커녕 다른 개발을 하느라 정신없었죠.
Java로 개발을 하다가 자주 마주치는 Exception 중 하나인 NullPointerException을 마주치면...
또야..?
출처 입력
하는 생각이 드실 거예요.
그리고 그것을 방지하기 위해 아래와 같은 코드를 많이 작성하셨을 거라고 생각해요.
if(version != null) { if(operationCode != null) { if(someIdea != null) { ...... } } }
Null check를 하기 위해서 개발자들은 위와 같은 코드들을 반복해서 작성하기 시작했습니다.
저런 코드는 자신이 짜고 싶지 않다고 해서 안 짤 수도 없습니다.
값이 없다는 것 자체를 표현하는 수단이 null이었으니까요.
Java8에서는 이러한 문제를 해결하기 위해 java.util.Optional을 추가하였습니다.
정리해보면, Java8 이전에 null에 관련된 문제는 크게 두 가지가 있었습니다.
1. Runtime에 NullPointException이라는 예외를 발생시킬 수가 있습니다.
2. NullPointException 방어를 위해 들어간 Null check 로직 때문에 코드 가독성과 유지 보수성이 떨어집니다.
Java 진영에서는 Java8에서부터 함수형 언어에서 그 해답을 찾았다고 봅니다.
스칼라나 히스 켈 과 같은 소위 함수형 언어들은 전혀 다른 방법으로 이 문제를 해결합니다.
Java가 "존재하지 않는 값"을 표현하기 위해서 Null을 사용했고 그것을 위해 개발자가 Null Check를 했다면
Optional은 "존재할 수도 있고 안 할 수도 있는 값"을 표현하기 위한 class라고봐주시면 될 것 같습니다.
말이 조금 어려운데 "Null 일 수도 있고 Null이 아닐 수도 있는 값을 다룰 수 있게 해주는" class라고봐주시면 됩니다. 결론은 Optional은 값을 포함하거나, 포함하지 않는 단일 값 컨테이너이고 Optional 값을 캡슐화하는 클래스입니다.
이 정도면 개념적인 부분은 충분히 설명이 되었다고 생각합니다.
백문이 불여일타라고 하죠?
이제 직접 한번 Optional 객체를 생성해보겠습니다.
Null을 담는 방법
Optional을 올바르게 사용하는 방법 1
1.Optional.empty()
Optional<String> name = Optional.empty(); System.out.println(name);
출력 결과는 아래와 같습니다.
Optional.empty
이제는 Null 대신에 위에 값을 사용해 주시면 될 듯합니다.
2.Optional.of()
null이 아닌 객체를 담고 있는 Optional 객체를 생성할 수도 있습니다.
null이 넘어올 경우 NullPointException을 던지기 때문에 주의해서 사용해야 합니다.
우선 예제를 보시면...
String testName = "Dokyeom"; Optional<String> realName = Optional.of(testName); System.out.println(realName);
위와 같습니다.
출력 결과는 아래와 같습니다.
Optional[Dokyeom]
만약 String이 null이라면 어떤 식으로 출력이 될지 궁금하실 겁니다.
String testName = null; Optional<String> realName = Optional.of(testName); System.out.println(realName);
그런 경우, NullPointException이 발생합니다.
Exception in thread "main" java.lang.NullPointerException at java.util.Objects.requireNonNull(Objects.java:203) at java.util.Optional.<init>(Optional.java:96) at java.util.Optional.of(Optional.java:108) at com.optional.Example.main(Example.java:13)
3. Optional.ofNullable()
null 인지 아닌지에 대해서 확신할 수 없는 객체를 담고 있는 Optional 객체를 생성합니다.
위의 1번과 2번을 합쳐놓은 메서드라고 생각하시면 됩니다.
Null이 넘어올 경우에는 NullPointException을 던지지 않고 Optional.empty()와 동일하게 비어있는 Optional 객체를 얻어옵니다.
저 같은 경우 DB에서 값을 긁어올 때 값이 없으면 null 값이 떨어지다 보니, null 인지 아닌지를 확신할 수가 없어서 어떠한 값을 감싸줄 때에 ofNullable을 가장 많이 사용하는 것 같습니다.
좌변에도 Optional로 감싸주셔야 하고, 우변에도 Optional 관련 처리를 해주셔야 합니다.
String testName = null; Optional<String> realName = Optional.ofNullable(testName); System.out.println(realName);
출력 결과는 아래와 같습니다.
Optional.empty
위와 같이 Null을 어느 그릇에 담아야 할지 느낌은 오셨을 것 같습니다.
그렇다면, 담은 곳에 접근해야 할 것입니다.
Optional에 접근하는 방법
Optional을 올바르게 사용하는 방법 2
Optional은 기본적으로 반환 타입을 위해 설계된 타입이기 때문에 Class 안의 filed로써 선언하는 것은 Optional의 설계 의도와 맞지 않습니다. 함수형 언어들을 보면 return 하는 순간에 값을 주렁주렁 매달아서 반환하는 출구 값을 보면 의도를 알 수가 있습니다.
1. get()
Optional로 감싼 값은, get() 메서드를 통해서 접근할 수 있습니다.
String testName = "bork"; Optional<String> realName = Optional.ofNullable(testName); System.out.println(realName.get());
출력 결과는 아래와 같습니다.
bork
2. orElse()
위에서 사용한 get() 메서드를 사용한 값이 만약 비어있다면 어떻게 할까요?
원래 반환되어야 할 값을 대신하여 어떤 값이 반환되어야 할 텐데요. 그럴 경우에 사용되는 값이 orElseXX 시리즈입니다. 아래 예제를 보시면 한 번에 이해가 가실 겁니다.
Optional<String> gender = Optional.of("나는남자"); Optional<String> emptyGender = Optional.empty(); System.out.println(gender.orElse("gender에 값이 없으면 내가 나오지롱")); System.out.println(emptyGender.orElse("emptyGender에 값이 없으면 내가 나오지롱")); System.out.println();
출력 결과는 아래와 같습니다.
나는남자 emptyGender에 값이 없으면 내가 나오지롱
3. orElseGet()
엇, 그렇다면 orElse와의 차이점은 어떻게 되나요?
두 메서드를 보면 반환값이 조금 다릅니다.
orElse()의 경우에는 반환값을 그대로 받는 반면에 orElseGet()은 Supplier로 매핑 한 값을 인자로 받습니다.
아래 예제를 보시면 한 번에 이해가 가실 겁니다.
Optional<String> gender = Optional.of("나는남자"); Optional<String> emptyGender = Optional.empty(); System.out.println(gender.orElseGet(() -> "gender에 값이 없으면 내가 나오지롱(람다)")); System.out.println(emptyGender.orElseGet(() -> "emptyGender에 값이 없으면 내가 나오지롱(람다)")); System.out.println(); System.out.println();
출력 결과는 아래와 같습니다.
나는남자 emptyGender에 값이 없으면 내가 나오지롱(람다) 내용을 입력하세요.
orElse 같은 경우에는 항상 함수가 실행되고, orElseGet 같은 경우 참조한 값이 null 일 경우에만 함수가 실행됩니다. orElseGet은 null 일 경우에만 함수가 실행되므로 성능상 이점이 있다고 말씀드릴 수 있습니다.
4. orElsetThrow()
"저는 어떠한 값을 반환시키고 싶은 게 아니라, Exception을 발생시키고 싶은 데요?"
할 때 사용하시는 메서드입니다.
아래 예제를 보시면 한 번에 이해가 가실 겁니다.
Optional<String> emptyGender = Optional.empty(); emptyGender.orElseThrow(NullPointerException::new);
출력은 아래와 같습니다.
Exception in thread "main" java.lang.NullPointerException at java.util.Optional.orElseThrow(Optional.java:290) at com.optional.Example.orElseThrowsExample(Example.java:10) at com.optional.Example.main(Example.java:48)
이번 포스팅에서 접근을 하는 방법까지 알아보았으니, 다음 시간에는 접근 후 어떤 식으로 값을 처리해야 되는지에 대해서 알아보겠습니다.
제가 이런저런 블로그를 많이 돌아다니면서 공부를 하다 보니, 명품 포스팅이 있어서 소개해드리고 싶습니다.
다른 분들도 물론 정말 포스팅을 많이, 잘 해주셨지만 특히나 Optional을 어떻게 사용해야 되나라는 주제로 감명 깊게 읽은 포스팅이 있어서 아래에 링크 걸어둘게요.
Optional 제대로 활용하기 - Increment (latera.kr)
감사합니다.
'Backend > JAVA&JAVA8' 카테고리의 다른 글