java.util.Optional Class
Optional<T> 클래스
는 'T'타입의 객체를 포장해주는 래퍼클래스(Wrapper Class)이다.
Optional<T> 클래스
는 null이 올 수 있는 값을 감싸는 클래스로, 만약 null이 저장되어 있더라도NullPointerException
이 발생하지 않는다.
public final class Optional<T> {
// If non-null, the value; if null, indicates no value is present
private final T value;
...
}
value
에 값을 저장하기 때문에 값이 null이라도 바로 참조 시에 NPE가 발생하지 않고, 또한 클래스이기 때문에 각종 메소드를 제공해준다.
자바에서는 기본 타입 스트림을 위한 별도의 Optional 클래스를 제공해준다.
- OptionalInt 클래스
- OptionalLong 클래스
- OptionalDouble 클래스
위의 클래스로 생성된 객체는 각각
getAsXXX()
메소드로 객체에 저장된 값에 접근할 수있다.
Optional 객체 생성
Optional 클래스는 객체 생성을 위한 3가지 정적 팩토리 메소드를 제공해준다.
Optional.empty()
null을 담고 있는, 비어있는 Optional 객체를 생성한다. 이 비어있는 객체는 Optional 내부적으로 미리 생성해놓은 싱글톤 인스턴스이다.
Optional<User> emptyOptional = Optional.empty();
Optional.of(value)
null이 아닌 객체를 담고 있는 Optional 객체를 생성한다. null이 넘어올 경우 NPE가 발생하기 때문에 null이 올 수 있을 경우엔
ofNullable(value)
를 써야한다.
Optional<User> ofOptional = Optional.of(user);
Optional.ofNullable(value)
null인지 아닌지 확신할 수 없는 객체를 담고 있는 Optional 객체를 생성한다.
Optional.empty()
와Optional.of(value)
를 합쳐놓은 메소드라고 생각하면 된다.null이 넘어오면,
Optional.empty()
와 동일하게 동작하고, null이 아닌 값이 넘어오면,Optional.of(value)
와 동일하게 동작한다.
Optional<User> nullableOptional1 = Optional.ofNullable(user);
Optional<User> nullableOptional2 = Optional.ofNullable(null);
Optional 객체 접근
Optional 클래스는 담고 있는 객체에 접근하기 위해 다양한 인스턴스 메소드를 제공한다.
아래 메소드들은 Optional이 담고 있는 값이 null 이 아닌 경우에는 동일하게 해당 값을 리턴한다.
하지만 Optional이 비어있는 경우(null을 담고 있는 경우), 각각 다르게 동작한다. 그 차이를 알아보자.
get()
- Optional 객체에 저장된 값이 존재하면, 그 값을 리턴한다.
- 비어있는 Optional 객체에 대하여
NoSuchElementException
을 던진다. 따라서,ifPresent()
를 통하여 값이 존재하는지를 먼저 체크한 뒤에 사용하여야 한다.
orElse(T other)
- Optional 객체에 저장된 값이 존재하면, 그 값을 리턴한다.
- 비어있는 Optional 객체에 대하여, 넘어온 인자를 리턴한다.
orElseGet(Supplier<? extends T> other)
- Optional 객체에 저장된 값이 존재하면, 그 값을 리턴한다.
- 비어있는 Optional 객체에 대하여, 넘어온 함수형 인자를 통해 생성된 객체를 리턴한다.
- Optional 객체가 비어있는 경우에만 함수가 호출되기 때문에
orElse(T other)
과 비교하여 성능상의 이점이 있을 수 있다.
orElseThrow(Supplier<? extends X> exceptionSupplier)
- Optional 객체에 저장된 값이 존재하면, 그 값을 리턴한다.
- 비어있는 Optional 객체에 대하여, 넘어온 함수형 인자를 통해 생성된 예외를 리턴한다.
Optional 사용
Optional 클래스를 사용하는
null handling을 직접 하지 않고, Optional 클래스에 위임
하기 위해서 이다. 따라서, Optional 클래스를 사용함에도, null을 처리한다면 Optional을 제대로 사용하고 있지 못하다고 볼 수 있다.
// Optional 사용 X
String text = getText();
int length;
if (text != null) {
length = maybeText.get().length();
} else {
length = 0;
}
// Optional 사용 O
String text = getText();
Optional<String> maybeText = Optional.ofNullable(text);
int length;
if (maybeText.isPresent()) {
length = maybeText.get().length();
} else {
length = 0;
}
Optional을 적절하게 사용한다 함은 아래와 같이 사용하는 것이다.
int length = Optional.ofNullable(getText()).map(String::length).orElse(0);
마찬가지로 다음 예제도 Optional을 어떻게 사용하냐에 따라 지저분하게 보일지, 아닐지가 결정된다.
// Optional 사용 X
User user = getUser();
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String street = address.getStreet();
if (street != null) {
return street;
}
}
}
return "주소 없음";
// Optional 사용 O
Optional<User> user = Optional.ofNullable(getUser());
Optional<Address> address = user.map(User::getAddress);
Optional<String> street = address.map(Address::getStreet);
String result = street.orElse("주소 없음");
// 더 간단하게
Optional<User> user = Optional.ofNullable(getUser())
.map(User::getAddress)
.map(Address::getStreet)
.orElse("주소 없음");
NullPointerException을 핸들링할 때에도, Optional의 elseThrow() 를 활용하여 간단하게 작성할 수 있다.
// Optional 사용 X
String value = null;
String result = "";
try {
result = value.toUpperCase();
} catch (NullPointerException e) {
throw new CustomExcpetion();
}
Optional<String> opt = Optional.ofNullable(null)
.orElseThrow(CustomException::new)
.toUpperCase();
Refer to
'Language > Java' 카테고리의 다른 글
[Java] 정규 표현식(Regular Expression) (0) | 2021.05.04 |
---|---|
[Java] 얕은 복사(Shallow Copy), 깊은 복사(Deep Copy) (0) | 2021.03.23 |
[Java] Stream API - 3. 스트림 최종연산 (0) | 2021.03.16 |
[Java] Stream API - 2. 스트림 중개연산 (0) | 2021.03.14 |
[Java] Stream API - 1. 스트림 생성 (0) | 2021.03.11 |