개발을 할 때 자주 신경쓰이는 부분이 바로 NPE, null 문제이다.
방어로직으로 null 검사를 많이 하게 되는데 if문을 자주 사용했었다. 하지만 이런 null 문제를 객체 차원에서 바로 예방이 되는 걸 사용하는 걸로 요즘은 권장하고 있다.
java8 버전에서 나온 Optional 클래스는 NPE 방지에 효과적이서 많이 쓰인다. null이 올 수 있는 값을 감싸는 Wrapper 클래스로 NPE가 발생하지 않게 하는 메서드들을 제공한다.
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
get()
Optional<String> stropt =
Arrays.asList(null, "kmb").stream().findFirst();
System.out.println("stropt = " + stropt.get());
// NPE 발생!
ifPresent()
중복 방어 로직으로도 많이 사용한다!
// 주로 jpa findById 유형 메서드의 중복을 확인할 때!
userRepository.findByEmail(req.getEmail())
.ifPresent(u -> {
throw new CalendarException(ErrorCode.USER_DUPLICATE);
});
orElse(인자)
인자
를 리턴하라.// 위의 코드를 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)
인자
를 하라. // 데이터가 존재하면 수정, 없으면 생성
Lawd saved = lawdRepository.findByLawdCd(lawd.getLawdCd())
.orElseGet(Lawd::new);
Optional.ofNullable(null).orElseGet(null);
// NPE 발생!
orElseThrow()
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로 감싸지 말 것.