[Java8 in Action] 10장. null 대신 Optioanl

null 대신 Optioanl

null은 구현하기 쉬웠기 때문에 존재했던 것이지, 의도했던 것은 아니다.

00. 값이 없는 상황을 어떻게 처리할까?

public String getCarInsuranceName(Person person){
  if(persion != null) {
    Car car = person.getCar();
    if(car != null) {
      Insurance insurance = car.getInsurance();
      if(insurance != null){
        return insurance.getName()
      }
    }
  }
  return "Unknown"
}

이렇게 코딩하고 있는가?

아니면…

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 "Unkwon";
  }
  return insurance.getName();
}

Null 때문에 발생하는 문제

Optional 클래스

java.util.Optional<T>

선택형 값을 캡슐화하는 클래스.

Optioanl.empty 는 Optioanl 의 특별한 싱글턴 인스턴스를 반환하는 정적 팩토리 메소드.

null 레퍼런스와 Optional.empty() 는 서로 무엇이 다를까?

null을 참조하려 하면 NullPointerException이 발생하지만 Optioanl.empty()는 Optioanl 객체이므로 이를 다양한 방식으로 활용할 수 있다.

Optioanl을 이용하면 값이 없는 상황이 우리 데이터에 문제가 있는 것인지 아니면 알고리즘의 버그인지 명확하게 구분할 수 있다. 모든 null 레퍼런스를 Optioanl로 대치하는 것은 바람직하지 않다. Optioanl 의 역할은 더 이해하기 쉬운 API를 설계하도록 돕는다.

즉, 메서드의 시그너처만 보고도 선택형 값인지 여부를 구별할 수 있다.

Map으로 Optional의 값을 추출하고 변환

보통 객체의 정보를 추출할 때는 Optioanl을 사용할 때가 값다.

String name = null;
if(insurance != null){
  name = insurance.getName();
}

이런 유형의 패턴에 사용할 수 있도록 Optional은 map 메서드 지원

Optional<Insurance> optInsurance = Optioanl.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

flatMap 으로 Optioanl 객체 연결

Optional<Person> optPerson = Optioanl.of(person);
Optional<String> name = optPerson.map(Person::getCar)
  															 .map(Car::getInsurance)
  															 .map(Insurance::getName);

와 같이 할 수 있지 않을까 생각할 수 있지만, 해당 코드는 컴파일되지 않는다.

왜 그럴까? 위 map을 여러번 사용하는 것은 마치 Optioanl<Optional<Car>> 형식의 객체이다.

getInsurance는 또 다른 Optioanl 객체를 반환하므로 getInsurance 메서드를 지원하지 않는다.

이런 문제는 어떻게 해결 할수 있을까?

Flatmap 메서드로

전달된 함수는 각각의 정사각형을 두 개의 삼각형을 포함하는 스트림으로 변환한다. 즉, map을 적용한 결과로 세 개의 스트림을 포함하는 하나의 스트림이 생성된다.

위 컴파일 되지 않는 코드는 어떻게 해결하면 될까?

pulbic String getCarInsuranceName(Optional<Person> person){
  return person.flatMap(Person::getCar)
    					 .flatMap(Car::getInsurance)
    					 .map(Insurance::getName)
    					 .orElse("Unknown");
}

flatmap메서드로 전달된 함수는 각각의 정사각형을 두 개의 삼각형을 포함하는 스트림으로 변환한다. 즉, map을 적용한 결과로 세 개의 스트림(각각 두 개의 삼각형을 가지고 있는)을 포함하는 하나의 스트림이 생성된다. 하지만, flatmap 덕분에 이차원 스트림이 여섯 개의 삼각형을 포함하는 일차원스트림으로 바뀐다.

디폴트 액션과 Optional 언랩

활용 예시

잠재적으로 null이 될 수 있는 대상을 Optional로 감싸기.

Object value = map.get("key");
Optioanl<Object> value = Optional.ofNullable(map.get("key"))

그 외에는 우리가 더 Optioanl을 잘 쓰기 위한 노력을 하면서 채워보자.