java.util.Optional 클래스
Optional 클래스는 Integer나 Double클래스처럼 'T'타입의 객체를 포장해주는 래퍼 클래스(Wrapper class)입니다.
따라서 Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있습니다.
public final class Optional<T> {
private final T value; // T타입의 참조 변수
}
최종 연산의 결과를 그냥 반환하는 게 아니라 Optional객체에 담아서 반환하는 것이다.
이처럼 객체에 담아서 반환을 하면, 반환된 결과가 null인지 매번 if문으로 체크하는 대신 Optional에 정의된 메서드를 통해서 간단히 처리할 수 있다.
이제 널 체크를 위한 if문 없이도 NullPointerException이 발생하지 않는 보다 간결하고 안전한 코드를 작성하는 것이 가능해진 것이다.
Optional객체를 생성할 때는 of() 또는 ofNullable()을 사용한다.
String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(new String("abc"));
만일 참조변수의 값이 null일 가능성이 있으면, of() 대신 ofNullable()을 사용해야한다.
of()는 매개변수의 값이 null이면 NullPointerException이 발생하기 때문이다.
Optional<String> optVal = Optional.of(null); //NullPointException발생
Optional<String> optVal = Optional.ofNullable(null); //Ok
Optional타입의 참조변수를 기본값으로 초기화할 때는 empty()를 사용한다.
null로 초기화하는 것이 가능하지만, empty()로 초기화 하는 것이 바람직하다.
Optional<String> optVal = null;; // 널로 초기화
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화
Optional객체에 저장된 값을 가져올 때는 get()을 사용한다. 값이 null일 때는 NoSuchElementException이 발생하며, 이를 대비해서 orElse()로 대체할 값을 지정할 수 있다.
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외발생
String str2 = optVal.orElse(); // optVal에 저장된 값이 null일 때에는, ""를 반환
orElse()의 변형으로는 null을 대체할 값을 반환하는 람다식을 지정할 수 있는 orElseGet()과 null일 때 지정된 예외를 발생시키는 orElseThrow()가 있다.
T orElseGet(Supplier<? extends T> other)
T orElseThrow(Supplier<? extends X> exceptionSupplier)
사용 방법은 아래와 같다.
String str3 = optVal2.orElseGet(STring::new); // () -> new String()과 동일
String str4 = optVal2.orElseThrow(NullPointerException::new); // 널이면 예외발생
Stream처럼 Optional객체에도 filter(), map(), flatMap()을 사용할 수 있다.
map()의 연산결과가 Optional<Optional>일 때, flatMap()을 사용하면 Optional를 결과로 얻는다. 만일 Optional객체의 값이 null이면, 이 메서드들은 아무 일도 하지 않는다.
int result = Optional.of("123")
.filter(x->x.length() > 0)
.map(Integer::parseInt).orElse(-1); // result = 123
result = Optional.of("")
.filter(x->x.length() > 0)
.map(Integer::parseInt).orElse(-1); // result = -1
우리가 이미 알고 있는 것처럼 parseInt()는 예외가 발생하기 쉬운 메서드이다. 만일 예외처리된 메서드를 만든다면 다음과 같을 것이다.
static int optStrToInt(Optional<String> optStr, int defaultValue) {
try {
return optStr.map(Integer::parseInt).get();
} catch (Exception e) {
return defaultValue;
}
}
isPresent()는 Optional객체의 값이 null이면 false를, 아니면 true를 반환한다.
ifPresent (Consumer block)은 값이 있으면 주어진 람다식을 실행하고, 없으면 아무 일도 하지 않는다.
if(str != null) {
System.out.println(str);
}
만일 위와 같은 조건문이 있다면, isPresent()를 이용하여 다음과 같이 쓸 수 있다.
if (Optional.ofNullable(str).isPresent()) {
System.out.println(str);
}
이 코드를 ifPresent()를 이용해서 바꾸면 더 간단히 할 수 있다. 아래의 문장은 참조변수 str이 null이 아닐 때만 값을 출력하고, null이면 아무 일도 일어나지 않는다.
Optional.ofNullable(str).ifPresent(System.out::println);
ifPresent()는 Optional를 반환하는 findAny()나 findFirst()와 같은 최종 연산과 잘 어울린다. Stream클래스에 정의된 메서드 중에서 Optional를 반환하는 것들은 다음과 같다.
Optional<T> findAny()
Optional<T> findFirst()
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)
Optional<T> reduce(BinaryOperator<T> accumulator)
이처럼 Optional를 결과로 반환하는 최종 연산 메서드들은 몇 개 없다.
심지어 max()와 min()같은 메서드들은 reduce()를 이용해서 작성된 것이다.
반환타입참고
optional클래스 | 값을 반환하는 메서드 |
---|---|
Optional | T get() |
OptionalInt | int getAsInt() |
OptionalLong | long getAsLong() |
OptionalDouble | double getAsDouble() |