[Java] Optional<T> NPE Handling Java 8

동민·2021년 12월 15일
0

1. orElse

    @Test
    public void test1(){
        String str = null;
        System.out.println(this.optionalTest(str));
    }

    @Test
    public void test2(){
        String str = "";
        System.out.println(this.optionalTest(str));
    }

    @Test
    public void test3(){
        String str = "hello";
        System.out.println(this.optionalTest(str));
    }

    private String optionalTest(String str){
        str = Optional.ofNullable(str).orElse("hello world");
        return str;
    }

test1

"hello world"

test2

""

test3

"hello"

2. orElseGet

orElse 보다 orElseGet 을 지향하자!

  • orElse(...)에서 ...Optional 값 유무와 상관없이 무조건 실행된다.
  • orElseGet(Supplier)에서 SupplierOptional에 값이 없을 때만 실행된다.

불필요한 오버헤드 없이 수행하기 위해서는 orElse() 대신 orElseGet()을 지향해야 한다.

// Bad
ItemDto itemDto = Optional.of(...)
   .map(...)
   .orElse(new ItemDto()); // Optional 값 유무와 상관없이 new ItemDto()는 무조건 실행됨 (생성 후 미사용 -> 불필요한 오버헤드)

// Good
ItemDto itemDto = Optional.of(...)
   .map(...)
   .orElseGet(ItemDto::new); // Optional 값이 없을 때만 new ItemDto()가 실행됨

Java Optional 바르게 쓰기

http://homoefficio.github.io/2019/10/03/Java-Optional-%EB%B0%94%EB%A5%B4%EA%B2%8C-%EC%93%B0%EA%B8%B0/

3. orElseThrow

Criteria.getItemCode() == null 일 때, new XXXException 발생

public void method() throws XXXException {
    // validation
    Optional.ofNullable(Criteria.getItemCode())
	.orElseThrow(() -> 
		new XXXException("error.validation.field", new String[] { "에러" }));
}

Criteria.getItemCode() == null 일 때, new Exception 발생

public void method() throws Exception {
    // validation
    Optional.ofNullable(Criteria.getItemCode())
	.orElseThrow(Exception::new)
    	.stream()
        .forEach(reviewNo -> {
        	// for-each logic
        });
}

4. map

public Json<?> getItem(Criteria Criteria) {
    // 데이터 조회 (1)
    Json<?> json = Optional.ofNullable(criteria)
			   .map((cri) -> { // cri: criteria
				return feignClient.getItemInfo(cri.getItemCode(), cri.getItemName());
			    })
			    .orElse(new Json<>());

    // 데이터 조회 (2)
    List<Info> info = new ArrayList<>();
    Optional.ofNullable(criteria)
	    .map((cri) -> { // cri: criteria
		return (List<Map<String, Object>>) feignClient.getInfo(cri.getItemCode(), cri.getItemName()).getResult();
	    })
	    .orElse(Collections.emptyList())
	    .forEach((map) -> { // map: .map((cri)->{return ...})) 의 리턴 값 (컬렉션이라면 컬렉션 요소)
	        Info if = Info.builder()
			      .img(String.valueOf(map.get("img")))
			      .link(String.valueOf(map.get("link")))
			      .build();
		info.add(if);
	    });
	    
    json.getResult().setInfo(info);
    return json;
}

maporElse

return Optional.ofNullable(criteria)	// (0) 크리테리아
	.map(this::getItem)		// (1)
	.map(OOORepository::selectItem)	// (2)
	.map((rep) -> {return ...;})	// (3)
	.orElse("XXX");
  • (0) -> (1) -> (2) -> (3) 차례대로 흐름을 타며 중간에 (0) 크리테리아 또는 (1), (2), (3) 로직 중 하나라도 null이 리턴 된다면 orElse 문에 선언한 "XXX" 가 리턴된다.

5. isPresent

Info info = XXX;
Optional.ofNullable(json.getResult())
	.ifPresent((result) -> { // result: json.getResult()
	    result.setInfo(info); // json.getResult() 가 null 이 아닐 때, 로직 수행
	});

// json.getResult() 가 null 일 경우, NPE를 피하고 위 로직은 타지 않음
profile
BE Developer

0개의 댓글