Monday
[Java8] Optional 이란? 본문
1. Optional이란?
Optional이란 Null 참조 문제를 해결하기 위해 Java8에 추가된 개념입니다. Optional<T> 형식으로, 어떤 값을 Optional이 감싸고 있는 형태로 사용됩니다. Optional로 감쌌다는 것은 T라는 값이 있을 수도 있고 없을 수도 있다는 의미를 나타냅니다. 즉, 포함할 수 있는 값은 1개 이하이며 T가 null일 수도 있고 null이 아닐 수도 있음을 의미하는데, 이를 적용함으로써 null 참조 오류를 줄일 수 있습니다. 예를 들어, Optional<String>에서 String 값이 null이라면 Optional<String>은 null인 값을 감싸고있는 Optional 객체입니다. 따라서, String 값이 null이라도 개발자는 Optional 객체를 참조하는 것이고, 이를 통해서 null 참조 오류를 해결할 수 있습니다.
2. Optional 객체 생성 방법
Optional 객체는 다음과 같이 생성 할 수 있습니다.
생성 방법 | 사용 시기 |
Optional.empty() | 빈 Optional 객체를 생성할 때 사용 |
Optional.of(value) | value 값이 null이 아니면 Optional 객체 생성. value 값이 null이라면 NullPointerException 발생. |
Optional.ofNullable(value) | value 값이 null이 아니면 Optional 객체 생성. value 값이 null이라면 빈 Optional 객체 생성. |
3. Optional 연산
Optional은 Stream과 유사하게 map, filter와 같은 연산을 제공합니다. 각 연산의 의미와 사용법은 다음과 같습니다.
자세한 내용은 java.util.Optional을 살펴보면 더 쉽게 이해하실 수 있습니다.
연산 | 의미 | 사용법 or 메소드 정의 |
get | 값이 존재하면 값 반환, 없으면 NoSuchElementException 발생 |
Optional<String> s = myString; String value = s.get(myString) |
filter | 값이 존재하고 조건과 일치하는 값이 있다면 Optional 객체 반환, 없으면 빈 Optional 객체 반환 |
Optional<person> p = myPerson; p.filter(p -> p.getAge() > minAge) |
ifPresent | 값이 존재하면 인자로 넘어 온 Consumer 함수 실행. 값이 없으면 아무 일 일어나지 않음 |
ifPresent(Consumer<? super T> action) |
ifPresentOrElse | 값이 존재하면 인자로 넘어 온 Consumer 함수 실행. 값이 없으면 emptyAction 실행 |
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) |
isPresent | 값이 존재하면 true 없으면 false | public boolean isPresent() { return value != null;} |
or | 값이 존재하면 자기 자신 Optional 반환. 값이 없으면 Supplier 실행한 결과를 반환 |
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) |
orElse | 값이 존재하면 값을 반환 값이 없으면 기본값 반환 |
public T orElse(T other) { return value != null ? value : other;} |
orElseGet | 값이 존재하면 값을 반환 값이 없으면 Supplier 적용한 결과 반환 |
public T orElseGet(Supplier<? extends T> supplier) { return value != null ? value : supplier.get();} |
orElseThrow | 값이 존재하면 반환. 없으면 NoSucheElementException 에러 발생 |
public T orElseThrow() { if (value == null) { throw new NoSuchElementException("No value present"); } return value;} |
4. Optional에서 map과 flatMap
Optional로 감싸진 Optional 객체에 map 메서드를 적용하면, 감싸진 값에 map 함수가 적용되고 그 결과를 새로운 Optional 객체가 감싼 결과가 반환됩니다. 반면에, flatMap 메서드는 적용 결과를 새로운 Optional 객체로 감싸지 않습니다. 이 차이를이해하기 쉽게 살펴보면, map 함수는 2번 연속해서 적용했을 때 결과가 Optional<Optional<T>>와 같은 결과가 나옵니다. 하지만, flatMap은 여러 번 적용해도 Optional<T> 형태만 나옵니다. 그 이유는 map 함수와 flatMap 함수의 정의를 보면 더 자세히 알 수 있습니다. 아래와 같이 map 메서드는 T -> U 로 변환하는 mapper가 인자(parameter)로 들어오고, 그 결과를 새로운 Optional 객체로 둘러싸서 반환합니다. 하지만, flatMap 메서드는 T -> Optional<U> 로 변환하는 mapper만 인자로 들어오고, 그 결과를 그대로 반환합니다. 따라서, map 메서드는 mapper가 여러 번 사용되면 Optional이 중첩된 결과를 만들 수 있지만, flatMap은 늘 Optional이 1개로만 감싸진 결과를 반환하게 됩니다.
// map 함수
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
// flatMap 함수
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
@SuppressWarnings("unchecked")
Optional<U> r = (Optional<U>) mapper.apply(value);
return Objects.requireNonNull(r);
}
}
5. Java 9 Optional의 stream 메서드
Java 9에서는 stream 처리 중 Optional을 포함하는 스트림이 등장할 때 편하게 처리할 수 있게 stream()이라는 메소드가 추가되었습니다. 보통 일반적인 스트림 요소를 조작하면 map, filter 등 여러 긴 체인을 거치는데 중간에 Optional이 등장하면 처리가 다소 복잡해집니다. Optional이 등장한다면, 우선 Optional 안에 감싸진 값이 있는지 확인해야하고, get함수를 이용해 감싸진 값을 가져와야 최종 Collection으로 반환할 수 있습니다. 이 과정을 stream 메서드를 이용해 단축시켜줍니다. 예를 통해 살펴보는 것이 이해가 쉬우므로 예제를 사용해 살펴보겠습니다.
Stream<Optional<String>> stream = .... // Optional 스트림 등장
Set<String> result = stream.filter(Optional::isPresent())
.map(Optional::get)
.collect(toSet());
//위와 동일한 동작을 하는 stream 메서드
Stream<Optional<String>> stream = .... // Optional 스트림 등장
Set<String> result = stream.flatMap(Optional::stream)
.collect(toSet());
'언어 > Java' 카테고리의 다른 글
2. Java 기본 문법(데이터 타입, 변수, 제어문) (0) | 2024.11.10 |
---|---|
1. 실무에서 자주 사용하는 Java 코딩 컨벤션 (0) | 2024.11.10 |
[Java8] Stream이란? (0) | 2022.03.13 |
[Java8] 함수형 인터페이스란? (0) | 2022.02.27 |
Java8 Unsigned (0) | 2021.08.01 |