[Java] Optional 사용법

devdo·2021년 8월 11일
0

Java

목록 보기
19/59
post-thumbnail

✨ NPE(Null Pointer Exception)

개발을 할 때 자주 신경쓰이는 부분이 바로 NPE, null 문제이다.
방어로직으로 null 검사를 많이 하게 되는데 if문을 자주 사용했었다. 하지만 이런 null 문제를 객체 차원에서 바로 예방이 되는 걸 사용하는 걸로 요즘은 권장하고 있다.

Optional이란?

java8 버전에서 나온 Optional 클래스는 NPE 방지에 효과적이서 많이 쓰인다. null이 올 수 있는 값을 감싸는 Wrapper 클래스로 NPE가 발생하지 않게 하는 메서드들을 제공한다.


Optional 생성

  • of()
   Optional.of(null).ifPresent(s-> System.out.println(s));   
   // NPE 발생함
  • ofNullable()
   Optional.ofNullable(null).ifPresent(System.out::println);      
   // NPE 발생하지 않음! null이면 아무것도 없는 빈값으로 반환

=> 그리고 이후에 Optional 접근 메서드orElse() 또는 orElseGet() 메소드를 이용해서 값이 없는 경우라도 안전하게 Optional 값을 가져올 수 있다.

  • empty()
   Optional<Car> optCar = Optional.empty();
   // NPE 발생하지 않음! 똑같이 null이면 아무것도 없는 빈값으로 반환
  • stream()
  Optional<String> stropt = 
  Arrays.asList("dsg", "kmb").stream().findFirst();
  // dsg

Optional 접근

  • get()
    : 이 메서드는 값을 가져오는 가장 간단한 메서드이면서 동시에 가장 안전하지 않은 메서드
        Optional<String> stropt =
                Arrays.asList(null, "kmb").stream().findFirst();

        System.out.println("stropt = " + stropt.get());
        // NPE 발생!
  • ifPresent()
    : 값이 존재하면 인수로 넘겨준 동작을 실행한다. null이면 아무 일도 일어나지 않는다.
    => ✨중복 방어 로직으로도 많이 사용한다!
// 주로 jpa findById 유형 메서드의 중복을 확인할 때!
   userRepository.findByEmail(req.getEmail())
       .ifPresent(u -> {
               throw new CalendarException(ErrorCode.USER_DUPLICATE);
            });
  • orElse(인자)
    : Optional에 값이 있으면 가져오고 null인 경우에 인자를 리턴하라.
// 위의 코드를 Optional로 펼쳐놓으면 아래와 같다.
Optional<UserVO> userVO = Optional.ofNullable(getUser());
Optional<Address> address = userVO.map(UserVO::getAddress);
Optional<String> postCode = address.map(Address::getPostCode);
String result = postCode.orElse("우편번호 없음");
 
// 그리고 위의 코드를 다음과 같이 축약해서 쓸 수 있다.
String result = user.map(UserVO::getAddress)
    .map(Address::getPostCode)
    .orElse("우편번호 없음");

// 주의! orElse(null) -> NPE 발생!
  • orElseGet(Supplier)
    : Optional에 값이 있으면 가져오고 null인 경우에 인자를 하라.
  // 데이터가 존재하면 수정, 없으면 생성
  Lawd saved = lawdRepository.findByLawdCd(lawd.getLawdCd())
                .orElseGet(Lawd::new);
                
  Optional.ofNullable(null).orElseGet(null);
  // NPE 발생!
  • orElseThrow()
    : Optional에 값이 있으면 가져오고 null인 경우 Exception을 던져라.
  Optional<String> nameOpt = Optional.ofNullable(getName());
  String result = nameOpt
  	.orElseThrow(CustomUpperCaseExcpetion::new).toUpperCase();
// 출처: https://mangkyu.tistory.com/70 [MangKyu's Diary] 
// 다른유형
	...
	.orElseThrow(() -> new CalendarException(ErrorCode.BAD_REQUEST));

정리

1) Optional 은 NPE 때문에 쓰는 객체이다.
Optional 오직 값 한 개가 들어있을 수도 없을 수도 있는 컨네이너이다.
2) 접근 메서드 정리 : get(), orElse(), orElseGet(), orElseThrow(),..
3) of() 보단 NPE가 발생하지 않는 ofnullable(), ifPresnt(), orElse() 를 많이 쓴다.

그리고 또 Optional이 만능은 아니다.
Optional을 사용하는 데 좋은 예와 나쁜 예가 있다.

그 예시는 다음 블로그를 참고하기 바란다.
https://www.latera.kr/blog/2019-07-02-effective-optional/


💥주의점을 정리하면,

Optional은

  • Return값으로만 쓰기를 권장한다. (메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말자.)
  • Optional을 리턴하는 메소드에서 null을 리턴하지 말자. ex. orElse(null)
  • 프리미티브 타입용 Optional을 따로 있다. OptionalInt, OptionalLong,...
  • Collection, Map, Stream Array, Optional은 Opiontal로 감싸지 말 것.


참고

profile
배운 것을 기록합니다.

0개의 댓글