Optional
개발자가 가장 곤란한 예외 중 하나인 NullPointerException(NPE). 이 예외를 피하고 null
을 체크하는 로직을 줄이기 위해 빈 값일 때 null
대신 초기값을 사용하길 권장하곤 합니다.
자바 8에서 추가된 Optional<T>
클래스를 이용해서 NPE
을 방지할 수 있습니다. Optional<T>
클래스는 한 마디로 null
이 올 수 있는 값을 감싸는 래퍼 클래스로 참조하더라도 null
이 일어나지 않도록 해주는 클래스입니다.
public final class Optional<T> {
// If non-null, the value; if null, indicates no value is present
private final T value;
...
}
생성하기
먼저 다음과 같이 빈 객체를 생성할 수 있습니다.
Optional<String> optional = Optional.empty();
System.out.println(optional); // Optional.empty
System.out.println(optional.isPresent()); // false
혹은 null
이 올 수 있는 값을 Optional<T>
로 감싸서 생성할 수 있습니다. orElse
또는 orElseGet
메소드를 이용해서 값이 없는 경우라도 안전하게 값을 가져올 수 있습니다.
다음 예제에서 getString
메소드가 리턴하는 값이 null
일 수도 있습니다. 하지만 Optional<T>
로 감싸면 됩니다.
// Optional 안에는 값이 있을 수도 있고 빈 객체일 수도 있다.
Optional<String> optional = Optional.ofNullable(getString());
String result = optional.orElse("other"); // 값이 없다면 "other" 를 리턴
사용하기
사용법은 간단합니다. 먼저 자바 8 이전에는 다음과 같이 null
체크가 필요했습니다.
// 자바 8 이전
List<String> list = getList();
List<String> listOpt = list != null ? list : new ArrayList<>();
Optional<T>
와 Lambda
를 이용하면 좀 더 간단하게 표현할 수 있습니다.
List<String> listOpt = Optional.ofNullable(getList()).orElseGet(() -> new ArrayList<>());
다음 코드는 null
체크 때문에 지저분해진 코드입니다.
User user = getUser();
if (user != null) {
Address address = user.getAddress();
if (address != null) {
String street = address.getStreet();
if (street != null) {
return street;
}
}
}
return "주소 없음";
위 코드처럼 null
이 올 수 있는 값을 하나하나 체크한다면 매우 지저분해지고 귀찮은 일이 될 것입니다.
map
메소드는 해당 값이 null
이 아니면 mapper
를 이용해 계산한 값을 저장하는 Optional객체
를 리턴합니다. 만약 값이 null이라면 빈 Optional 객체
를 리턴합니다.
public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
map
메소드를 이용해서 간단하게 표현해보겠습니다.
Optional<User> user = Optional.ofNullable(getUser());
Optional<Address> address = user.map(User::getAddress);
Optional<String> street = address.map(Address::getStreet);
String result = street.orElse("주소 없음");
// 다음과 같이 축약해서 쓸 수 있다.
user.map(User::getAddress)
.map(Address::getStreet)
.orElse("주소 없음");
만약 위 예제에서 getAddress
와 getStreet
가 Optional<T>
객체를 리턴한다면 flatMap
메소드를 사용할 수도 있습니다.
Optional<User> user = Optional.ofNullable(getUser());
String result = user
.flatMap(User::getAddress)
.flatMap(Address::getStreet)
.orElse("주소 없음");
다음 예제는 NPE
을 핸들링하는 예제입니다.
String value = null;
String result = "";
try {
result = value.toUpperCase();
} catch (NullPointerException e) {
throw new CustomExcpetion();
}
위와 같은 코드는 Optional<T>
을 이용하면 다음과 같이 표현할 수 있습니다.
String value = null;
Optional<String> valueOpt = Optional.ofNullable(value);
String result = valueOpt.orElseThrow(CustomExcpetion::new).toUpperCase();
'언어 ⏎ > java' 카테고리의 다른 글
정규표현식 (0) | 2023.02.07 |
---|---|
Stream (0) | 2022.12.30 |
더블콜론과 람다 (0) | 2022.12.30 |
람다(Lambda) (0) | 2022.12.30 |
XML parsing (0) | 2022.12.29 |