[Java8 In Action] #10 null 대신 Optional |
Java8 In Action 내용을 보며 정리한 내용입니다.
정리자는 기존에 Java8 을 한차례 rough 하게 공부한 적이 있고, Kotlin 역시 공부한 적이 있습니다.
위의 prerequisite 가 있는 상태에서 추가적인 내용만 정리한 내용이므로, 제대로 공부를 하고 싶다면 책을 구매해서 보길 권장합니다!
10.1. 값이 없는 상황을 어떻게 처리할까?
10.1.1. 보수적인 자세로 NullPointerException 줄이기
10.1.2. null 때문에 발생하는 문제
-
에러의 근원이다. : NullPointerException 은 자바에서 가장 흔히 발생하는 에러다.
코드를 어지럽힌다 : 중첩된 null 확인 코드.
아무 의미가 없다. : null 은 아무 의미도 표현하지 않는다. 값이 없음을 표현하는 방법으로 적절치 않다.
자바 철학에 위배된다 : 자바는 개발자로부터 모든 포인터를 숨겼다. 예외가 있다면 바로 null
형식 시스템에 구멍을 만든다 : null 은 무형식이며 정보를 포함하고 있지 않다.
10.1.3. 다른 언어는 null 대신 무얼 사용하나?
-
그루비 같은 언어에서는 안전 내비게이션 연산자(safe navigation operator ?.) 을 도입해서 null 문제 해결
def carInsuranceName = person?.car?.insurance?.name
-
하스켈은 선택형값(optional value)을 저장할 수 있는 Maybe 라는 형식을 제공한다.
주어진 형식 값을 갖거나 아무 값도 갖지 않을 수도 있다.
스칼라도 T 형식의 값을 갖거나 아무 값도 갖지 않을 수 있는 Option[T] 라는 구조를 제공한다.
-
자바8에서는 Optional<T> 를 제공한다.
10.2. Optional 클래스 소개
10.3. Optional 적용 패턴
10.3.1. Optional 객체 만들기
* 빈 Optional
-
Optional<Car> optCar = Optional.empty();
* null 이 아닌 값으로 Optional 만들기
-
Optional<Car> optCar = Optional.of(car); // car 가 null 이면 NPE
* null 값으로 Optional 만들기
-
Optional<Car> optCar = Optional.ofNullable(car); // car 가 null 이라도 exception 이 없다.
10.3.2. 맵으로 Optional 값을 추출하고 변환하기.
-
Optional<Insurance> optInsurance = Optional.ofNullable(insurance); Optional<String> name = optInsurance.map(Insurance::getName);
optInsurace 값이 비어있으면 아무일도 일어나지 않고, 값이 있으면 mapping 을 한다.
10.3.3. flatMap 으로 Optional 객체 연결
-
Optional<Person> optPerson = Optional.of(person); Optional<String> name = optPerson.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName);
여기서 optPerson.map(Person::getCar) 의 값은 Optional<Optional<Car>> 가 된다.
Optional 의 map 은 Optional 의 안쪽 내용물을 return 값으로 바꾸는 형태이다.
* Optional 로 자동차의 보험회사 이름 찾기
-
public String getCarInsuranceName(Optional<Person> person){ return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse(“Unknown”) }
* Optional 을 이용한 Person/Car/Insurance 참조 체인.
-
Optional 클래스는 필드 형식으로 사용할 것을 가정하지 않아 Serializable 인터페이스를 구현하지 않는다.
그래서 직렬화 모델을 사용하는 도구나 프레임워크에서 문제가 생길 수 있다.
직렬화가 필요하다면 Optional 로 값을 반환받을 수 있는 메서드를 추가하는 방식을 권장한다.
public class Person{ private Car car; public Optional<Car> getCarAsOptional(){ return Optional.ofNullable(car); } }
10.3.4. 디폴트 액션과 Optional 언랩
-
Optional 이 비어있을 때 디폴트값을 제공할 수 있는 orElse 로 값을 읽는 것이 좋다.
-
get 은 값을 읽는 가장 간단한 메서드이면서 동시에 가장 안전하지 않은 메서드이다.
메서드 get 은 래핑된 값이 있으면 해당 값을 반환하고 값이 없으면 NoSuchElementException 을 발생시킨다.
-
orElseGet(Supplier<? extends T> other)는 orElse 메서드에 대응하는 게으른 버전의 메서드이다.
-
orElseThrow(Supplier<? extends X> exceptionSupplier) 는 Optional 이 비어있을 때 예외를 발생시킨다. 단, get 과는 다르게 exception 을 바꿀 수 있다.
-
ifPresent(Consumer<? super T> consumer)를 이용하면 값이 존재할 때 인수로 넘겨준 동작을 수행할 수 있다.
10.3.5. 두 Optional 합치기
-
public Optional<Insurance> nullSafeFindCheapestInsurace(Optional<Person> person, Optional<Car> car){ if (person.isPresent() && car.isPresent()){ return Optional.of(findCheapestInsurance(person.get(), car.get())); }else{ return Optional.empty(); } }
-
위의 코드는 아래와 같이 바꿀 수 있다.
return person.flatMap(p -> car.map(c -> findCheapestInsurance(p,c)));
10.3.6. 필터로 특정값 거르기
-
optInsurace.filter(insurace -> “CambridgeInsurance”.equals(insurance.getName()) .ifPresent( x -> System.out.println(“ok”));
-
Optional 클래스의 메서드
empty
filter
flatMap
get
ifPresent
isPresent
map
of
ofNullable
orElse
orElseGet
orElseThrow
10.4. Optional 을 사용할 실용 예제
10.4.1. 잠재적으로 null 이 될 수 있는 대상을 Optional 로 감싸기
10.4.2. 예외와 Optional
* 기본형 Optional 과 이를 사용하지 말아야 하는 이유
-
스트림처럼 Optional 도 기본형 특화된 OptionalInt, OptionalLong, OptionalDouble 등의 클래스를 제공한다.
Optional 의 경우 최대 요소 수는 한개이므로 Optional 에서는 기본형 특화 클래스로 성능을 개선할 수 없다.
-
기본형 특화 Optional 은 10.2 절에서 살펴본 Optional 클래스의 유용한 메서드 map, filterMap, filter 등을 지원하지 않으므로 기본형 특화 Optional 을 사용할 것을 권장하지 않는다.
게다가 스트림과 마찬가지로 기본형 특화 Optional 로 생성한 결과는 다른 일반 Optional 과 혼용할 수 없다.
10.4.3. 응용
10.5. 요약
-
역사적으로 프로그래밍 언어에서는 null 레퍼런스로 값이 없는 상황을 표현해왔다.
-
자바 8에서는 값이 있거나 없음을 표현할 수 있는 클래스 Optional<T> 를 제공한다.
-
팩토리 메서드 Optional.empty, Optional.of, Optional.ofNullable 등을 이용해서 Optional 객체를 만들 수 있다.
-
Optional 클래스는 스트림과 비슷한 연산을 수행하는 map, flatMap, filter 등의 메서드를 제공한다.
-
Optional 로 값이 없는 상황을 적절하게 처리하도록 강제할 수 있다.
즉 Optional 로 예상치 못한 null 예외를 방지할 수 있다.
-
Optional 을 활용하면 더 좋은 API 를 설계할 수 있다.
즉, 사용자는 메서드의 시그너처만 보고도 Optional 값이 사용되거나 반환되는지 예측할 수 있다.
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
[Java8 In Action] #12 새로운 날짜와 시간 API (0) | 2019.01.01 |
---|---|
[Java8 In Action] #11 CompletableFuture: 조합할 수 있는 비동기 프로그래밍 (0) | 2018.12.31 |
[Java8 In Action] #9 디폴트 메서드 (0) | 2018.12.29 |
[Java8 In Action] #8 리팩토링, 테스팅, 디버깅 (0) | 2018.12.28 |
[Java8 In Action] #7 병렬 데이터 처리와 성능 (0) | 2018.12.27 |
댓글