본문으로 바로가기

Java8 Optional이란?

많은 존재 이유가 있지만 if(obj == null){}else{} 이 코드를 한줄로 깔끔하게 처리하기 위해 존재한다.

이걸 깔끔하게 처리 했을 때 functional programming에서 코드를 이쁘게 짤 수 있다.

Optional<String> optHello = Optional.ofNullable("hello");
System.out.println(optHello);

Optional<String> optNull = Optional.ofNullable(null);
System.out.println(optNull);

결과

Optional[hello]
Optional.empty

코드1

@Test
public void optionalTest() {
    Optional<String> opt = Optional.empty();
    System.out.println(opt.get());
}

결과

java.util.NoSuchElementException: No value present

위 코드는 에러가 난다.

@Test
public void optionalTest() {
    Optional<String> opt = Optional.ofNullable(new String());
    System.out.println(opt.get());
}

결과

""

위 코드는 .ofNullable을 이용해 if(aa == null){} else{} 이 코드를 한줄로 표현 할 수 있다.

Optional은 Java8에서 Java의 Null Pointer Exception을 피하기 위해 만든 클래스이다.

직접 쓰기 보다는 method를 만들 때 Optional로 감싸주면 Null Pointer Exception을 예방 할 수 있다.

1
2
3
4
5
private Optional<Thread> findThreadByName(long gameId){
        return Thread.getAllStackTraces().keySet().stream()
                .filter(thread -> String.valueOf(gameId).equals(thread.getName()))
                .findAny();
}
cs

위와 같이 Thread를 Return하는 예제인데 찾은 Thread가 없을 경우 Optional로 감싸주면 뒤에서 처리하기가 좋다.

코드1의 optionalEmpty()는 실행하면 java.util.NoSuchElementException: No value present 에러가 난다.

Optional이 Empty인 상태에서 .get()을 쓰면 에러가 나기 때문에 .isPresent()로 체크를 한 뒤에 쓰거나

[코드1]의 8번줄 처럼 .ifPresent(Consumer) 를 써준다.

동작은 if(Optional == null){}과 유사하게 작동 하지만 Optional을 써주면 좀 더 깔끔하게 처리 된다.

## Java8 Optional쓰는 법

UserDetailInfo USERDetailInfo1 = new UserDetailInfo();  
String aa = Optional.ofNullable(USERDetailInfo1)  
        .map(UserDetailInfo::getOrg)  
        .map(Org::getLocation)  
        .map(Location::getName)  
        .orElse("");  
assertEquals("", aa);
```java

이제 앱을 새로 개발을 하면 null을 최대한 안쓰고자 노력을 하는 중이다.

jackson으로 json을 pojo에 매핑해서 쓰는 경우가 많은데 json에 2레벨, 3레벨에 데이터가 없는 경우 NPE(Null Pointer Exception)가 나는 경우가 많다.

그래서 이걸 Optional을 안쓰고 하려면 각각 .get을 할 때마다 if(obj != null) 을 해주어야 하는데 이게 단계가 깊어지면 코드가 지저분해져서 처리 하기가 복잡해지고 배포를 하고 나서 장애가 난 후에야 비로소 코딩을 안하게 되는 문제가 있다.

그래서 Optional을 쓰면 .map()이걸로 하면 각 단계에서 Optional, Optional이 리턴 되고 마지막 .orElse()에서 default로 ""를 넣어 주기 때문에

String aa에 null이 들어갈 일이 없다.(믿자 믿습니다.)

## Java8 Stream sort(정렬)

.sort()와 Comparator를 사용한다.

<table class="colorscripter-code-table" style="margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;" cellspacing="0" cellpadding="0"><tbody><tr><td style="padding: 6px; border-right: 2px solid #e5e5e5;"><div style="margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;"><div style="line-height: 130%;">1</div><div style="line-height: 130%;">2</div><div style="line-height: 130%;">3</div></div></td><td style="padding: 6px 0;"><div style="margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;"><div style="padding: 0 6px; white-space: pre; line-height: 130%;">List<span style="color: #a71d5d;">&lt;</span>Vote<span style="color: #a71d5d;">&gt;</span>&nbsp;votes&nbsp;<span style="color: #a71d5d;">=</span>&nbsp;<span style="color: #a71d5d;">new</span>&nbsp;ArrayList<span style="color: #a71d5d;">&lt;</span><span style="color: #a71d5d;">&gt;</span>();</div><div style="padding: 0 6px; white-space: pre; line-height: 130%;">&nbsp;</div><div style="padding: 0 6px; white-space: pre; line-height: 130%;">votes.sort(Comparator.comparing(Vote::getSum).reversed());</div></div></td><td style="vertical-align: bottom; padding: 0 2px 4px 0;"><a style="color: white;" href="http://colorscripter.com/info#e" target="_blank" rel="noopener"><span style="font-size: 9px; word-break: normal; background-color: #e5e5e5; border-radius: 10px; padding: 1px;">cs</span></a></td></tr></tbody></table>

sum을 기준으로 내림차순 정렬

조건 2개일 때

<table class="colorscripter-code-table" style="margin: 0; padding: 0; border: none; background-color: #fafafa; border-radius: 4px;" cellspacing="0" cellpadding="0"><tbody><tr><td style="padding: 6px; border-right: 2px solid #e5e5e5;"><div style="margin: 0; padding: 0; word-break: normal; text-align: right; color: #666; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;"><div style="line-height: 130%;">1</div><div style="line-height: 130%;">2</div><div style="line-height: 130%;">3</div><div style="line-height: 130%;">4</div><div style="line-height: 130%;">5</div></div></td><td style="padding: 6px 0;"><div style="margin: 0; padding: 0; color: #010101; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; line-height: 130%;"><div style="padding: 0 6px; white-space: pre; line-height: 130%;">List<span style="color: #a71d5d;">&lt;</span>Vote<span style="color: #a71d5d;">&gt;</span>&nbsp;votes&nbsp;<span style="color: #a71d5d;">=</span>&nbsp;<span style="color: #a71d5d;">new</span>&nbsp;ArrayList<span style="color: #a71d5d;">&lt;</span><span style="color: #a71d5d;">&gt;</span>();</div><div style="padding: 0 6px; white-space: pre; line-height: 130%;">&nbsp;</div><div style="padding: 0 6px; white-space: pre; line-height: 130%;">votess.sort(Comparator.comparing(Vote::getSum).reversed()</div><div style="padding: 0 6px; white-space: pre; line-height: 130%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.thenComparing(Comparator.comparing(Vote::getName).reversed())</div><div style="padding: 0 6px; white-space: pre; line-height: 130%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);</div></div><div style="text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;"><a style="color: #e5e5e5;" href="http://colorscripter.com/info#e" target="_blank" rel="noopener">Colored by Color Scripter</a></div></td><td style="vertical-align: bottom; padding: 0 2px 4px 0;"><a style="color: white;" href="http://colorscripter.com/info#e" target="_blank" rel="noopener"><span style="font-size: 9px; word-break: normal; background-color: #e5e5e5; border-radius: 10px; padding: 1px;">cs</span></a></td></tr></tbody></table>

## java8 function이란?

<table class="colorscripter-code-table" style="box-sizing: border-box; border-collapse: collapse; border-spacing: 0px; background-color: #fafafa; margin: 0px; padding: 0px; border: none; border-radius: 4px;" cellspacing="0" cellpadding="0"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td style="box-sizing: border-box; padding: 6px; border-right: 2px solid #e5e5e5;"><div style="box-sizing: border-box; margin: 0px; padding: 0px; word-break: normal; text-align: right; color: #666666; line-height: 18.2px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;"><div style="box-sizing: border-box; line-height: 18.2px;">1</div><div style="box-sizing: border-box; line-height: 18.2px;">2</div><div style="box-sizing: border-box; line-height: 18.2px;">3</div><div style="box-sizing: border-box; line-height: 18.2px;">4</div><div style="box-sizing: border-box; line-height: 18.2px;">5</div><div style="box-sizing: border-box; line-height: 18.2px;">6</div><div style="box-sizing: border-box; line-height: 18.2px;">7</div><div style="box-sizing: border-box; line-height: 18.2px;">8</div><div style="box-sizing: border-box; line-height: 18.2px;">9</div><div style="box-sizing: border-box; line-height: 18.2px;">10</div><div style="box-sizing: border-box; line-height: 18.2px;">11</div><div style="box-sizing: border-box; line-height: 18.2px;">12</div><div style="box-sizing: border-box; line-height: 18.2px;">13</div><div style="box-sizing: border-box; line-height: 18.2px;">14</div><div style="box-sizing: border-box; line-height: 18.2px;">15</div></div></td><td style="box-sizing: border-box; padding: 6px 0px;"><div style="box-sizing: border-box; margin: 0px; padding: 0px; color: #010101; line-height: 18.2px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;"><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;"><span style="box-sizing: border-box; color: #a71d5d;">public</span>&nbsp;Integer&nbsp;getInteger(<span style="box-sizing: border-box; color: #066de2;">String</span>&nbsp;string)&nbsp;{</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box; color: #a71d5d;">return</span>&nbsp;Integer.<span style="box-sizing: border-box; color: #066de2;">parseInt</span>(string);</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">}</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">Function<span style="box-sizing: border-box; color: #a71d5d;">&lt;</span><span style="box-sizing: border-box; color: #066de2;">String</span>,&nbsp;Integer<span style="box-sizing: border-box; color: #a71d5d;">&gt;</span>&nbsp;getInteger2&nbsp;<span style="box-sizing: border-box; color: #a71d5d;">=</span>&nbsp;str&nbsp;<span style="box-sizing: border-box; color: #a71d5d;">-</span><span style="box-sizing: border-box; color: #a71d5d;">&gt;</span>&nbsp;Integer.<span style="box-sizing: border-box; color: #066de2;">parseInt</span>(str);</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">@Test</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;"><span style="box-sizing: border-box; color: #a71d5d;">public</span>&nbsp;<span style="box-sizing: border-box; color: #a71d5d;">void</span>&nbsp;function()&nbsp;{</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box; color: #066de2;">int</span>&nbsp;result&nbsp;<span style="box-sizing: border-box; color: #a71d5d;">=</span>&nbsp;getInteger(<span style="box-sizing: border-box; color: #63a35c;">"10"</span>);</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box; color: #066de2;">int</span>&nbsp;result2&nbsp;<span style="box-sizing: border-box; color: #a71d5d;">=</span>&nbsp;getInteger2.apply(<span style="box-sizing: border-box; color: #63a35c;">"10"</span>);</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box; color: #066de2;">System</span>.<span style="box-sizing: border-box; color: #066de2;">out</span>.<span style="box-sizing: border-box; color: #066de2;">println</span>(result);</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box; color: #066de2;">System</span>.<span style="box-sizing: border-box; color: #066de2;">out</span>.<span style="box-sizing: border-box; color: #066de2;">println</span>(result2);</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">&nbsp;</div><div style="box-sizing: border-box; padding: 0px 6px; white-space: pre; line-height: 18.2px;">}</div></div><div style="box-sizing: border-box; text-align: right; margin-top: -13px; margin-right: 5px; font-size: 9px; font-style: italic;"><a style="box-sizing: border-box; background-color: transparent; color: #e5e5e5;" href="http://colorscripter.com/info#e" target="_blank" rel="noopener">Colored by Color Scripter</a></div></td><td style="box-sizing: border-box; padding: 0px 2px 4px 0px; vertical-align: bottom;"><a style="box-sizing: border-box; background-color: transparent; color: white;" href="http://colorscripter.com/info#e" target="_blank" rel="noopener"><span style="box-sizing: border-box; font-size: 9px; word-break: normal; background-color: #e5e5e5; border-radius: 10px; padding: 1px;">cs</span></a></td></tr></tbody></table>

1~3번 줄과 같은 형태의 string을 받아서 int를 리턴하는 식의 메소드를 많이 많드는데 이걸 인터페이스로 빼놓은게 function이다.

좀 특이한점은 .apply()로 실행을 한다는 것이다. 람다는 일종의 익명클래스라서 익명클래스 안에 있는 .apply()를 실행해준다고 생각하면 된다.

## groupBy

Map<String, BigDecimal> map = trades.stream()
.collect(Collectors.groupingBy(Trade::getBuyUserId, Collectors.reducing(BigDecimal.ZERO, Trade::getAmount, BigDecimal::add)));
```

groupBy 하고 싶을 때 이걸 쓴다.

end


댓글을 달아 주세요