자바로 개발을 하다 보면 NPE를 겪게 된다. 자바 8부터는 이 NPE를 어떻게 처리할까?
값이 없는 상황 처리 하기
public String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getName();
}
}
}
return "Unknown";
}
- 위 예제 에서는 변수에 접근할 때마다 null 체크를 하기 때문에 코드가 지저분해진다.
- null 때문에 발생하는 문제가 많기 때문에 사용해서는 안된다.
- 자바는 개발자로부터 모든 포인터를 숨겼다. 하지만 예외가 Null이다.
- Null은 무형식이며 정보를 포함하지 않고 있기 때문에 모든 참조 형식에 Null을 할당할 수 있다.
Optional 클래스
- Optional은 선택형 값을 캡슐화하는 클래스이다.
- 값이 있으면 Optional 클래스는 값을 감싸고, 값이 없으면 Optional.empty 메서드로 Optional 객체를 반환한다.
public class Person {
private Optional<Car> car;
public Optional<Car> getCar() {
return car;
}
}
public class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() {
return insurance;
}
}
- Optional <car> 이런 식으로 명시하면 사람이 자동차를 소유했을 수도 아닐 수도 있다는 것을 나타낸다.
Optional 적용 패턴
Optional 객체 만들기
- 빈 Optional: Optional.empty로 빈 Optional 객체를 얻을 수 있다.
- null이 아닌 값으로 Optional 만들기: 정적 팩토리 메서드 of를 활용하여 null이 아닌 값을 포함하는 Optional을 만들 수 있다. Optional.of(car);
- Null 값으로 Optional 만들기: Optional.ofNullable(car) 이런 식으로 null값을 저장할 수 있는 Optional을 만들 수 있다.
맵으로 Optional의 값을 추출하고 변환하기
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
- Optional은 map 메서드를 지원해주기 때문에 이런 식으로 쉽게 값을 추출할 수 있다.
flatMap으로 Optional 객체 연결
Optional<Person> optionalPerson = Optional.of(person);
String name = optionalPerson.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("UNKNOWN");
Optioanl 스트림 조작
Stream<Optional<String>> optionalStream = persons.stream()
.map(Person::getCar)
.map(optCar -> optCar.flatMap(Car::getInsurance))
.map(optInsurance -> optInsurance.map(Insurance::getName));
Set<String> collect = optionalStream.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
- 자바 9에서는 스트림을 쉽게 처리할 수 있도록 Optional에 stream() 메서드를 추가했다.
- 다음 코드 처럼 filter를 이용하여 결과를 얻을 수 있다.
Optional 클래스의 다양한 메서드
static <T> Optional<T> empty() | 아무런 값도 가지지 않는 비어있는 Optional 객체를 반환함. |
T get() | Optional 객체에 저장된 값을 반환함. |
boolean isPresent() | 저장된 값이 존재하면 true를 반환하고, 값이 존재하지 않으면 false를 반환함. |
static <T> Optional<T> of(T value) | null이 아닌 명시된 값을 가지는 Optional 객체를 반환함. |
static <T> Optional<T> ofNullable(T value) | 명시된 값이 null이 아니면 명시된 값을 가지는 Optional 객체를 반환하며, 명시된 값이 null이면 비어있는 Optional 객체를 반환함. |
T orElse(T other) | 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환함. |
T orElseGet(Supplier<? extends T> other) | 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 람다 표현식의 결괏값을 반환함. |
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) |
저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 예외를 발생시킴. |
정리
- null이 될 수 있을 경우 Optional.ofNullable를 이용하자.
- Integer.parseInt와 같은 예를 보면 문자열을 정수로 반환할 수 없는 경우 NumberFormatException을 반환한다.
public static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberForamtException e) {
return Optional.empty();
}
}
'자바 > 모던 자바 인 액션' 카테고리의 다른 글
[모던 자바 인 액션] 13장. 디폴트 메서드 (0) | 2023.02.24 |
---|---|
[모던 자바 인 액션] 12장 새로운 날짜와 시간 API (0) | 2023.02.07 |
[모던 자바 인 액션] 10장. 람다를 이용한 도메인 전용 언어 (0) | 2022.12.27 |
[모던 자바 인 액션] 9장. 리팩터링, 테스팅, 디버깅 (0) | 2022.11.18 |
[모던 자바 인 액션] 8장. 컬렉션 API 개선 (0) | 2022.11.10 |