💡 이 장의 내용
- null 참조의 문제점과 null을 멀리해야 하는 이유
- null 대신 Optional: null로부터 안전한 도메인 모델 재구현하기
- Optional 활용 : null 확인 코드 제거하기
- Optional에 저장된 값을 확인하는 방법
- 값이 없을 수도 있는 상황을 고려하는 프로그래밍
자바로 프로그램을 개발하면서 한 번쯤 마주하게 되는 NullPointerException..
11.1 값이 없는 상황을 어떻게 처리할까?
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
코드에는 아무 문제가 없는 것처럼 보이지만 차를 소유하지 않은 사람도 많다. 이때 getCar 를 호출하면 어떻게 될까?
11.1.1 보수적인 자세로 NullPointerException 줄이기
예기치 않은 널포인터 예외를 피하기 위해 대부분의 프로그래머는 필요한 곳에서 다양한 null 확인 코드를 추가해서 null 예외 문제를 해결하려 할 것이다.
// 제목: 너무 많은 출구
public String getCarInsuranceName(Person person) {
if(person == null) {
return "Unknown";
}
Car car = person.getCar();
if(car == null) {
return "Unknown";
}
Insurance insurance = car.getInsurance();
if(insurance == null) {
return "Unknown";
}
return insurance.getName();
}
11.1.2 null 때문에 발생하는 문제
- 에러의 근원이다.
- 코드를 어지럽힌다.
- 아무 의미가 없다.
- 자바 철학에 위배된다: 자바는 개발자로부터 모든 포인터를 숨겼다. 하지만 예외가 바로 Null 포인터다.
- 형식 시스템에 구멍을 만든다.
11.1.3 다른 언어는 null 대신 무얼 사용하나?
그루비 같은 언어에서는 안전 내비게이션 연산자(?.)를 도입해서 null 문제를 해결했다.
def carInsuranceName = person?.car?.insurance?.name
히스켈, 스칼라 등의 함수형 언어는 아예 다른 관점에서 접근한다. 히스켈은 Maybe라는 형식을 제공하고 스칼라는 Option[T] 구조를 제공한다. 자바에서는 히스켈과 스칼라의 영향을 받아서 java.util.Optional<T>라는 새로운 클래스를 제공한다.
11.2 Optional 클래스 소개
값이 있으면 Optional 클래스는 값을 감싼다. 반면 값이 없으면 Optional.empty 메서드로 Optional을 반환한다.
11.3 Optional 적용 패턴
11.3.1 Optional 객체 만들기
- 빈 Optional: 정적 팩토리 메서드 Optional.empty 로 빈 옵셔널 객체를 얻을 수 있다.
Optional<Car> optCar = Optional.empty();
- null이 아닌 값으로 Optional 만들기
Optional<Car> optCar = Optional.of(car);
이제 car가 null이라면 즉시 널포인터 에러가 발생한다.
- null값으로 Optional 만들기
Optional<Car> optCar = Optional.ofNullable(car);
널 값을 저장할 수 있는 팩토리 메서드이다. 이제 car가 null이면 빈 옵셔널 객체를 반환한다.
11.3.2 맵으로 Optional의 값을 추출하고 변환하기
기존의 널인지 확인하고 객체에 접근하는 방식을 옵셔널을 사용할 때는 이렇게 쓸 수 있다.
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
11.3.3 flatMap으로 Optional 객체 연결
Optional<Person> optPerson = Optional.of(person);
Optional<String> name = optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);
안타깝게도 위 코드는 컴파일되지 않는다. 왜 그럴까? 맵의 연산 구조가 Optiona<Optional<Car>>를 반환하기 때문이다.
Optional로 자동차의 보험회사 이름 찾기
flatMap을 사용하여 앞선 문제를 해결할 수 있다.
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
'Book > 모던 자바 인 액션' 카테고리의 다른 글
[모던 자바 인 액션] Chapter 12. 새로운 날짜와 시간 API (0) | 2025.01.12 |
---|---|
[모던 자바 인 액션] Chapter 11. null 대신 Optional 클래스 (2) (1) | 2024.11.20 |
[모던 자바 인 액션] Chapter 10.람다를 이용한 도메인 전용 언어 (1) (1) | 2024.11.14 |
[모던 자바 인 액션] Chapter 09. 리팩터링, 테스팅, 디버깅 (2) (4) | 2024.11.12 |
[모던 자바 인 액션] Chapter 09. 리팩터링, 테스팅, 디버깅 (1) (4) | 2024.11.11 |