※ 일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.
Optional이란?
Optional은 값을 랩핑하여 그 값을 null일지도 모른다는 것을 표시하시는 클래스이다.
사용법
메소드 getHoge()는 null을 반환하는 경우가 있다고 상정한다. 지금까지라면 다음과 같이 null체크를 했을 것이라고 생각된다.
String hoge = getHoge(); // hoge는 null일지도
if (hoge != null) { // null체크
System.out.println(hoge.length()); // hoge가 null이 아니므로 length메소드를 호출
}
null일지도 모르는 변수 hoge의 메소드를 호출하는 경우, 사전에 null체크가 필요하지만, 구현 중 빠트려버리고 마는 경우가 있을지도 모른다. 이것을 Optional을 사용하여 바꿔 쓰면 다음과 같이 쓸 수 있다.
Optional<String> hogeOpt = Optional.ofNullable(getHoge()); // 값을 랩핑한다.
hogeOpt.ifPresent(hoge -> System.out.println(hoge.length())); // 값이 존재하는 경우에만 처리된다.
Optional을 사용하는 것으로 값이 존재하지 않은 경우를 잊지 않고 인식하는 동시에, 사전 null 체크 없이 코드를 쓸 수 있게 된다. null체크의 if문으로 줄바꿈이 많아지는 것도 없다.
ofNullalble메소드로 인수의 값을 가진 Optional형의 오브젝트를 작성한다. ifPresent메소드는 가지고 있는 값이 존재하고 있는 경우, 즉 null이 아닌 경우만 인수의 람다식 (Consumer형)을 실행한다.
람다식의 인수 hoge는 갖고 있는 실제의 값이다.
여기서는 설명을 위해, getHoge를 호출한 결과를 Optinal화하고 있지만, 원래 getHoge내부에서 Optinal화하여 반환하는 것이 효과적이다. 이와 관련해서는 후술하도록 하겠다.
처리한 결과를 반환하기
ifPresent메소드는 값을 반환하지 않는다. Optional의 값을 사용한 처리의 결과, 값을 반환하고 싶은 경우는 map메소드를 사용한다.
Optional<String> hogeOpt = Optional.ofNullable(getHoge());
Optional<Integer> lengthOpt = hogeOpt.map(hoge -> hoge.length());
map메소드의 반환값은 람다식의 반환값을 랩핑한 Optional이 된다. hogeOpt의 값이 존재하지 않는 경우는 람다는 실행되지 않고, 값이 없는 Optional 오브젝트가 반환된다.
또한, 2개의 Optional오브젝트가 동시에 값을 가진 경우만 처리 결과를 반환하도록 하고 싶은 경우는, flatMap메소드를 사용하면 된다.
// opt1와 opt2에 값이 존재하는 경우에만, 처리 결과를 반환한다.
Optional<String> opt1 = getHogeOpt();
Optional<String> opt2 = getHogeOpt();
Optional<String> opt3 = opt1.flatMap(str1 ->
opt2.flatMap(str2 -> {
return Optional.of(str1 + str2);
}));
flatMap은 map과 달리 반환값을 스스로 Optional오브젝트로 해야할 필요가 있다는 점이다.
동일한 처리는 map으로 하고자 하면, 반환값은 Optional<Optional<String>>이 되어버린다.
filter
위에서 본 map및 flatMap메소드는 동일한 이름의 메소드가 Stream인터페이스에도 정의되어 있어 작성방법은 동일하다.
Optional메소드에서 Stream의 메소드와 동일한 이름을 가진 것은 그 외에도 filter메소드가 존재한다.
Optional<String> hogeOpt = Optional.ofNullable(getHoge());
Optional<String> filteredOpt = hogeOpt.filter(hoge -> hoge.length() >= 2);
filter의 조건에 부합하는 경우는 자신(이 경우는 hogeOpt)를 반환하고, 조건에 맞지 않은 경우는 값이 없는 Optional오브젝트가 반환된다.
Optiona에서 값을 획득하기
Optional에서 값을 획득할 때는 주로 orElse혹은 orElseGet메소드를 사용하게 된다. orElse의 인수는 Optional이 값을 갖지 않은 경우의 기본값을 지정한다.
Optional<String> hogeOpt = Optional.ofNullable(getHoge());
// orElse
String hoge = hogeOpt.orElse("기본값");
// orElseGet
String hoge2 = hogeOpt.orElseGet(() -> "생성비용이 비싼 기본값");
orElseGet의 인수도 값이 없는 경우의 기본값을 지정하지만, Supplier형의 람다식으로 전달한다. orElseGet메소드의 기본값은 람다식을 사용하여 지정하고 있으므로, 실제로 필요하게 될 때까지 생성되지 않는다.
기본 메소드?
or, isPresent, get이라는 어떤 의미로 기본적인 메소드가 있지만, 아래에서 기재했듯, 이용할 수 있는 경우는 꽤 제한적이다.
// Optional오브젝트를 생성하는 of메소드는, 인수가 null이면 NullPointerException를 던진다.
Optional<String> hogeOpt = Optional.of(getHoge());
if (hogeOpt.isPresent()) { // 사전에 굳이 값의 존재여부를 체크하고 있다.
// 값을 얻는 get메소드는 값이 존재하지 않는 경우 실행예외를 던진다.(NoSuchElementException)
String hoge = hogeOpt.get();
System.out.println(hoge);
}
Optional를 사용해야 할 부분
Optional이 주로 사용되는 곳은 메소드의 반환값이다. 원래라면 null을 반환하는 메소드의 반환값을 Optional형으로 하는 것으로 값이 존재하지 않는 것을 명시적으로할 수 있다.
성질을 명시하는 것 자체는 안전한 프로그램을 구축하기 위해 중요하다.이를 전제로,ifPresent메소드나 map메소드로 역시 안전하고 스마트하게 처리를 구현할 수 있다.
'IT > 언어' 카테고리의 다른 글
[JUnit] JUnit5의 테스트 클래스 작성 (0) | 2023.01.31 |
---|---|
[Java] Stream (filter, map, forEach, reduce등) (0) | 2023.01.30 |
[Vue.js] Vue Router과 간단한 사용법 (0) | 2023.01.16 |
[Vue.js] export default에 대해서 (0) | 2023.01.16 |
[Vue.js] watch 오브젝트 (handler 유무, deep, immediate등) (0) | 2023.01.15 |