관리 메뉴

생각해보기

모던 자바 인 액션 -9- 본문

자바

모던 자바 인 액션 -9-

정한_s 2021. 12. 19. 16:50

자바와 Optional

자바에서 null 참조를 사용하면서 발생할 수 있는 문제점

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

자바8은 java.util.Optional이라는 새로운 클래스를 제공한다. 값이 있으면 Optional 클래스는 값을 감싼다. 반면 값이 없으면 Optional.empty 매서드로 Optional을 반환한다. Optional을 이용하면 값이 없는 상황이, 우리 데이터에 문제가 있는 것인지 아니면 알고리즘의 버그인지 명확하게 구분할 수 있다.

 

Optional 객체 만들기

다양한 방법으로 Optional 객체를 만들 수 있다

Car car = new Car();
// 빈 Optional 
Optional<Car> optionalEmptyCar = Optional.empty();
// null 이 아닌 값으로 Optional 만들기, car가 null이라면 NullPointerException 발생
Optional<Car> optionalCar = Optional.of(car);
// null 값으로 Optional 만들기 
// ofNullable로 null을 저장할 수 있는 Optional을 만들 수 있다
// car가 null 이면 빈 Optional 객체가 반환된다
Optional<Car> optionalCarWithEmpty = Optional.ofNullable(car);

맵으로 Optional 사용

// 스트림 적용 전
Insurance insurance = new Insurance();
String name = null;
if(insurance != null){
    insurance.getName();
}

// Optional은 stream과 같이 map, flatMap 메서드 지원 
// Optional이 값을 포함하면 map의 인수로 제공된 함수가 값을 바꾼다 ex) Insurance ->String
// Optional이 값이 비어있으면 아무 일도 일어나지 않는다.
Optional<Insurance> insuranceOptional = Optional.ofNullable(insurance);
Optional<String> insuranceNameOptional = insuranceOptional.map(Insurance::getName);

 

* 도메인 모델에 Optional을 사용했을 때 데이터를 직렬화 할 수 없다

Optional 클래스는 필드 형식으로 사용할 것을 가정하지 않았으므로 Serializable 인터페이스를 구현하지 않았다.

* 자바 직렬화, 역직렬화란 ?

더보기

 

Optional 디폴트 액션 : Optional.get() , Optional.orElseGet , Optional.(...) 

  • get() : 값을 읽는 가장 간단한 메서드 면서 가장 안전하지 않은 메서드, get은 래핑된 값이 있으면 값을 반환하고 값이 없을 경우 NoSuchElementException을 발생시킨다
  • orElse(T t) :  값이 있을 때 값을 반환하고 값이 없을 경우 기본 값(t)을 제공하는 함수
  • orElseGet(Supplier<? extends T> other) : orElse 메서드에 대응하는 게으른 버전의 메서드. Optional에 값이 없을 때만 Supplier가 실행된다. 
  • orElseThrow(Supplier ? extends X > exceptionSupplier) : Optional이 비어있을 때 예외를 발생시킨다. 발생시킬 예외의 종류를 선택할 수 있다
  • ifPresent(Consumer<? super T> consumer) :  값이 존재할 때 인수로 넘겨준 동작을 실행할 수 있다. 값이 없으면 아무일 도 일어나지 않는다
  • map, filter, flatMap 등... (stream 과 방식과 동일)

Optional 정리

매서드 정리
empty 빈 Optional 인스턴스 반환
filter 값이 존재하며 프레디케이트와 일치시, 값을 포함하는 Optional을 반환하고, 값이 없으면 빈 Optional 반환
flatMap 값이 존재하면 인수로 제공된 함수를 적용한 결과 Optional을 반환하고, 값이 없으면 빈 Optional을 반환
get 값이 존재하면 Optional이 감싸고 있는 값을 반환하고, 값이 없으면 NoSuchElementException이 발생
ifPresent 값이 존재하면 지정된 Consumer를 실행하고, 값이 없으면 아무일도 일어나지 않음
ifPresentOrElse 값이 존재하면 지정된 Consumer를 실행하고, 값이 없으면 아무일도 일어나지 않음
isPresent 값이 존재하면 true 반환, 없으면 false 반환
map 값이 존재하면 제공된 매핑 함수 적용
of 값이 존재하면 값을 감싸는 Optional 반환, 값이 null이면 NullPointerException 발생
ofNullable 값이 존재하면 값을 감싸는 Optional 반환, 값이 null 이면 빈 Optional 반환
or 값이 존재하면 같은 Optional 반환하고, 값이 없으면 Suppiler에서 만든 Optional 반환
orElse 값이 존재하면 값을 반환, 값이 없으면 기본값 반환
orElseGet 값이 존재하면 값을 반환, 값이 없으면 Suppiler에서 제공하는 값 반환
orElseThrow 값이 존재하면 값을 반환, 값이 없으면 Suppiler에서 생성한 예외 발생
stream 값이 존재하면 존재하는 값만 포함하는 stream을 반환하고 값이 없으면 빈 스트림을 반환

 

Optional을 사용한 실용 예제

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

Map<String,Object> map = new HashMap<>();
Object value  = map.get("key"); // 값이 없을 경우 null 반환
Optional<Object> valueOptional = Optional.ofNullable(map.get("value")); // Optional 객체 반환

예외와 Optional 클래스 

값을 제공할 수 없을 때 null을 반환하는 대신 빈 Optional로 해결

// 문자열을 정수 Optional로 반환
public static Optional<Integer> stringToInt(String s){
    try{
        return Optional.of(Integer.parseInt(s));
    }catch (NumberFormatException e){
        return Optional.empty();
    }
}

 

사용 예제

// Optional 적용 전
public int readDuration(Map<String,String> map, String name){
    String value = map.get(name);
    if(value != null){
        try{
            int i = Integer.parseInt(value);
            if(i>0) return i;
        }catch (NumberFormatException e){}
    }
    return 0; // 조건 실패시 null 반환
}
// Optional 적용 후
public int readDurationOpt(Map<String,String> map, String name) {
    return Optional.ofNullable(map.get(name))
            .flatMap(JavaWithOptional::stringToInt)
            .filter(i -> i > 0)
            .orElse(0);
}

 

'자바' 카테고리의 다른 글

모던 자바 인 액션 -11-  (0) 2021.12.20
모던 자바 인 액션 -10-  (0) 2021.12.19
모던 자바 인 액션 -8-  (0) 2021.12.17
모던 자바 인 액션 -7-  (0) 2021.12.17
모던 자바 인 액션 -6-  (0) 2021.12.10
Comments