본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[Java8 In Action] #10 null 대신 Optional

by 돼지왕 왕돼지 2018. 12. 30.
반응형

[Java8 In Action] #10 null 대신 Optional



Java8 In Action 내용을 보며 정리한 내용입니다.

정리자는 기존에 Java8 을 한차례 rough 하게 공부한 적이 있고, Kotlin 역시 공부한 적이 있습니다.

위의 prerequisite 가 있는 상태에서 추가적인 내용만 정리한 내용이므로, 제대로 공부를 하고 싶다면 책을 구매해서 보길 권장합니다!


?., Empty, empty optional, exception change, Filter, flatmap, Get, ifpresent, ispresent, java8 in action, java8 optional, map, maybe, nosuchelementexception, NPE, NullPointerException, of, ofnullable, optinal value, option, optional, optional get, optional get nosuchelementexception, optional method, optionaldouble, optionalint, optionallong, orelse, orelseget, orelsethrow, safe navigation operator, supplier, 기본형 optional, 선택형값, 하스켈


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 값이 사용되거나 반환되는지 예측할 수 있다.




반응형

댓글