#11장 null 대신 Optional 클래스
Raoul-Gabriel Urma 저, 우정은 역 「Modern Java in Action, 2019」를 읽고 정리하였습니다.
11장 매일 자바와 함께
Optional: 더 좋은 API를 설계하고 널 포인터 예외를 줄이는 데 도움을 준다.
이 장의 내용
null 참조의 문제점과 null을 멀리해야 하는 이유
null 대신 Optional : nuII로 부터 안전한 도메인 모델 재구현하기
Optional 활용 : null 확인 코드 제거하기
Optional에 저장된 값을 확인하는 방법
값이 없을 수도 있는 상황을 고려하는 프로그래밍
팩트
널포인터 에러: 너무 많이 겪었다...
자바에서 null 포인터를 만든 것은 실수
null 때문에 어떤 문제가 발생할 수 있는지 간단한 예제로 살펴보자.
11.1 값이 없는 상황을 어떻게 처리할까?
public class Person {
private Car car;
public Car getCar() { return car; }
}
public class Car {
private Insurance insurance;
public Insurance getInsurance() { return insurance; }
}
public class Insurance {
private String name;
public String getName() { return name; }
}
// -----------------------------------
// 아래 메소드를 실행시 무슨 문제가 있을까?
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}보수적인 자세로 NullPointerException 줄이기
deep deoubt (깊은 의심): 아래 if문 반복된 패턴
짧게말해, null로 값이 없다는 사실을 표현하는 것은 좋은 방법이 아니다.
다른 언어는 null 대신 무얼 사용하나?
다른 언어 null 문제 해결 예시
하스켈: Maybe
스칼라: Option[T]
그루비: 안전 내비게이션 연산자(?.)
11.2 Optional 클래스 소개
자바에선, java.util.Optional
Optional 클래스가 객체가 있든 null이든 감싸준다.
모든 null 참조를 Optional로 대치하는 것 은 바람직하지 않다. Optional의 역할은 더 이해하기 쉬운 API를 설계하도록 돕는 것이다. 즉, 메서드의 시그니처만 보고도 선택형값인지 여부를 구별할 수 있다.
Optional 객체 만들기
Optional로 감싼 값을 실제로 어떻게 사용할 수 있을까? 일단, Optional 객체를 만들어보자.
get() 을 이용하여 Optional의 값을 가져올 수 있는데, Optional이 비어있으면 호출시 예외가 발생한다. 즉, Optional 사용시 결국 null을 사용했을 때 와 같은 문제를 겪을 수 있다.
맵으로 Optional의 값을 추출하고 변환하기
이거 먼말인지 잘모르겠음 ~!!$@#$%!@#%@#$!@$
flatMap으로 Optional 객체 연결
스트림의 flatMap 메서드와 Optional의 flatMap 메서드는 유사하다.
Optional로 자동차의 보험회사 이름 찾기
호출의 논리적 과정
1단계: Person에 Function을 적용 (getCar 메서드가 펑션)
flatmap 사용 이유: Optional이 계속 중첩됨으로 평준화 한다.
평준화 과정: 두 Optional을 합치는 기능을 수행하면서 둘 중 하나라도 null이면 빈 Optional을 생성
2단계: 1단계와 동일하게 작동하고, Optional 리턴
3단계: 스트링 반환하므로, flatmap 사용 필요 x
도메인 모델에 Optional을 사용했을 때 데이터를 직렬화할 수 없지만, 이런 단점에도 저자 Optional을 사용해서 도메인 모델을 구성하는 것이 바람직하다고 말한다.
Optional 스트림 조작
자바9, Optional에 stream() 메서드를 추가
flatMap(Optional::stream): Stream<Optional>을 현재 이름을 포함하는 Stream으로 변환
Optional에서 stream 메서드를 제공하지 않았다면, 이렇게 직접 처리해줘야하는 번거로움이 있었을것이다. Optional의 stream 메서드 안을 살펴보면 isPresent가 들어가있다.
디폴트 액션과 Optional 언랩
Optional unwrap 방법들
orElse(): 값이 없을때 기본값 제공.
get(): 값 바로 제공. 값이 없다면, NoSuchElementException. 그러니 조심 사용.
orElseGet(): 값이 없을때만, Supplier가 실행. Optional이 비어있을 때만 기본값을 생성하고 싶을때 사용.
orElseThrow(): get과 비슷하지만, 예외를 설정할 수 있다.
ifPresent(): 값이 존재할 때 인수로 넘겨준 동작을 실행. 없으면 실행 x.
ifPresentOrElse(): Optional이 비었을 때 실행할 수 있는 Runnable을 인수로 받는다는 점만 ifPresent와 다르다.
두 Optional 합치기
복잡한 비즈니스 로직 가정: 가장 보험료 저렴한 회사 찾기
퀴즈
Optional 언랩하지 않고 두 Optional 합치기
필터로 특정값 거르기
객체의 메서드 호출해서 어떤 프로퍼티를 확인해야 할 때가 있다.
filter 사용: 조건에 맞으면 값을 반환하고 그렇지 않으면 빈 Optional 객체를 반환
퀴즈
Optional filtering
특정 나이 이상만 되는 사람들이 가입한 보험을 찾아와 보자. (minAge 이상 일때만)
정답
11.4 Optional을 사용한 실용 예제
기존의 코드를 재구현하는데 도움이 되는 다양한 기법을 확인해보자.
https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
잠재적으로 null이 될 수 있는 대상을 Optional로 감싸기
예외와 Optional 클래스
Integer.parseInt()는 값을 제공할 수 없을 때 null을 반환하는 대신 예외를 발생시킨다.
대표적으로 숫자가 아닐때 NumberFormatException
기본형 Optional을 사용하지 말아야 하는 이유
Optionallnt, OptionalLong, OptionalDouble은 사용 자제하기.
map, flatMap,filter 등을 지원하지 않는다.
기본형은 기존 Optional 혼용 사용 불가
예, OptionalInt를 반환한다면 이를 다른 Optional 의 flatMap에 메서드 참조로 전달할 수 없다.
응용
실제 업무에서 어떻게 사용하는지 보자.
퀴즈
유틸리티 메서드를 이용해, 위 코드를 개선해보자.
Optional로 프로퍼티에서 지속 시간 읽기
Last updated