null이 발생할 수 있는 곳에 null 확인코드를 추가해서 처리할 수 있다.
public String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getName();
}
}
}
return "Unknown";
}
null 확인 코드 때문에 들여쓰기 수준이 증가하고 가독성도 좋지않다. 어떤 곳에서 null이 발생할지 확신하지 못한다면 모든 곳에 null 체크를 해줘야한다.
이런식으로 작성하여 들여쓰기 수준을 줄일 수 있지만 return을 하는 곳이 4군데나 되기 때문에 유지보수하기 힘들다.
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();
}
null을 사용하면 어떤 문제가 발생할 수 있을까?
자바 8부터 null 대신 사용하기 위한 Optional< T >를 제공한다. Optional은 선택형 값을 캡슐화하는 클래스다. Optional은 값이 하나 있거나 없다. 값이 있다면 Optional 클래스는 값을 감싸며, 값이 없는 경우 Optional.empty 메서드로 Optional을 반환한다.
Optional.empty는 NullPointerException을 발생시키지 않는다. Optional을 사용하면 해당 타입의 반환 값은 Optional<타입>
을 반환하게된다. 값이 없을 수도 있다는 것을 명시적으로 나타내는 것이다.
Optional을 이용하면 값이 없는 상황이 데이터에 문제가 있는 것인지 아니면 알고리즘의 버그인지 명확하게 구분할 수 있게된다. 모든 null 참조를 Optional로 대치하는 것은 바람직하지 않다.
Optional.empty();
Optional.of();
Optional.ofNullable();
(null이라면 빈 Optional 객체 반환)Optional을 최대 요소 개수가 한 개 이하인 데이터 컬렉션으로 생각할 수 있다. Optional의 map연산은 스트림과 유사하다.
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
Optional의 map을 연속적으로 사용할 수 없다.
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.map(Person::getCar) // Optional<Optional<Car>> 반환
.map(Car::getInsurance) // 컴파일 불가
.map(Insurance::getName);
이런 문제를 해결하기 위해 flatMap을 사용한다. 스트림의 flatMap처럼 이차원을 일차원으로 평준화시켜준다.
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName);
자바 9부터 Optional에 stream() 메서드가 추가됐다. Optional 스트림을 값을 가진 스트림으로 변환할 때 이 기능을 유용하게 활용할 수 있다.
Optional의 filter 메서드는 Predicate을 인수로 받는다. Optional 객체가 값을 가지며 Predicate와 일치하면 filter 메서드는 그 값을 반환하고 그렇지 않으면 빈 Optional 객체를 반환한다. Optional이 비어있다면 아무 동작도 하지 않고, 값이 있다면 값에 Predicate을 적용한다. 만약 false가 결과라면 빈 상태가 된다.
Optional<Object> value = Optional.ofNullable(map.get("key"));
public static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
Optional도 기본형 특화 클래스가 존재하지만, Optional의 최대 요소 수는 한 개이므로 Optional에서는 기본형 특화 클래스로 성능을 개선할 수 없다. 기본형 특화 Optional은 map, flatMap, filter등을 지원하지 않는다.
반대 의견: 9. Optional 대신 OptionalInt, OptionalLong, OptionalDouble