티스토리 뷰

Java/기본기

[JAVA8] 6. Optional

Jason of the Argos 2022. 4. 17. 20:53

NullPointerException(NPE)

Java 에서 Optional 이 등장한 이유는 바로 NullPointerException 때문이다. NPE로 인해 발생하는 문제들은 다음과 같이 정리된다.

  • 에러의 근원이다 : NPE는 자바에서 가장 흔히 발생하는 에러이다.
  • 코드를 어지럽힌다 : 때로는 중첩된 null 확인 코드를 추가해야 하므로 null 때문에 코드 가독성이 떨어진다.
  • 아무 의미가 없다 : null은 아무 의미도 표현하지 않는다. 특히 정적 형식 언어에서 값이 없음을 표현하는 방법으로는 적절하지 않다.
  • 자바 철학에 위배된다 : 자바는 개발자로부터 모든 포인터를 숨겼다. 하지만 예외가 있는데 그것이 바로 null 포인터다.
  • 형식 시스템에 구멍을 만든다 : null은 무형식이며 정보를 포함하고 있지 않으므로 모든 참조 형식에 null을 할당할 수 있다. 이런 식으로 null이 퍼졌을 때 애초에 null이 어떤 의미로 사용되었는지 알 수 없다.

즉 정리하자면 Null Pointer가 너무 방대한 의미를 내포하고 있기 때문에 발생하는 문제라고 할 수 있다.

 

Optional 클래스

하스켈, 스칼라 영향을 받은 Optional<T>가 등장한다. Optional의 가장 큰 특징은 선택형 값을 캡슐화하는 클래스라는 점이다.

 

 

값이 있으면 Optional 클래스는 값을 감싼다. 반면 값이 없으면 Optional.empty 메서드로 Optional을 반환한다. Optional.empty는 Optional의 특별한 싱글턴 인스턴스를 반환하는 정적 팩토리 메서드이다. null참조와 Optional.empty에는 차이가 있다. null을 참조하려하면 NPE이 발생하지만 Optional.empty()는 Optional 객체이므로 이를 다양한 방식으로 활용할 수 있다.

 

Optional 적용 패턴

 

1) 빈 Optional

Optional<Car> optCar = Optional.empty()

2) null이 아닌 Optional

Optional<Car> optCar = Optional.of(car);

3) null인 Optional

Optional<Car> optCar = Optional.ofNullable(car);

4) Optional의 map

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

map을 통해 반환되는 객체 또한 Optional에 감싸져 반환된다 (Optional<String>).

5) Optional의 flatMap

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

6) Optional 스트림 조작

Stream<Optional<String>> stream = ...;
Set<String> result = stream.filter(Optional::isPresent)
        .map(Optional::get)
        .collect(toSet());

 

디폴트 액션과 Optional 언랩

  • get : 값을 읽는 가장 간단한 메서드면서 동시에 가장 안전하지 않은 메서드이다. 값이 없으면 NoSuchElementException을 뱉기에 값이 반드시 있다고 가정할 수 있는 상황이 아니면 get메서드를 사용하지 말자
  • orElse : Optional이 값을 포함하지 않을 때 기본값을 제공할 수 있다.
  • orElseGet(Supplier<? extends T> other) : orElse 메서드에 대응하는 게으른 버전의 메서드이다. Optional에 값이 없을 때만 Supplier가 실행된다.
  • orElseThrow(Supplier<? extends X> exceptionSupplier) : Optional이 비어있을 때 예외를 발생시킬 수 있으며, 발생시킬 예외의 종류를 정할 수 있다.
  • ifPresent(Consumer<? super T> consumer) : 값이 존재할 대 인수로 넘겨준 동작을 실행할 수 있다. 값이 없으면 아무일도 일어나지 않는다.
  • (자바 9) ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) : Optional이 비었을 때 실행할 Runnable을 인수로 받는다