무언가의 단점을 보완하여 새롭게 나타난 기술이라고 해서 기존의 모든 기능을 보완하고 대체할 수 있는 것은 아닙니다. 람다 역시 기존의 모든 코드를 대체하고자 등장한 것이 아니기 때문에, 람다를 쓰는 것보다 기존의 코드 방식을 사용하는 것이 더 나은 경우도 많습니다. 따라서 이 글에서는 람다의 단점에 대해 살펴보도록 하겠습니다.
(1) 가독성
대부분 람다의 장점에서 빠지지 않는 것이 간결성과 가독성입니다. 그러나 아이러니하게도 단점에서 역시 가독성이 빠지지 않습니다. 람다를 알고 있는 사람이라면 가독성이 장점이 되겠지만, 람다에 대해 잘 모르는 사람에게는 가독성이 나쁜 코드가 되는 것입니다. 예를 들어 1부터 10까지 출력하는 아래와 같은 코드가 있습니다.
for(int i=0; i<10; i++){
System.out.println(i);
}
위 코드는 람다식으로 아래와 같이 변경할 수 있습니다.
IntStream.range(0,10).forEach((int value) -> System.out.println(value));
또는 메서드 레퍼런스를 사용하면 더욱 간단하게도 표현이 가능합니다.
IntStream.range(0,10).forEach(System.out::println);
단순한 코드임에도 람다로 표현하는 것이 더 간결하게 보이지는 않습니다. 이는 람다를 이용한 코드가 이런 방식의 단순 반복 코드에는 적합하지 않기 때문입니다. 지난 뉴스클리핑에서 다룬 바와 같이 필요한 만큼만 스트림에서 요소를 사용하고자 하는 것도, 지연 연산이 필요한 것도, 병렬 처리가 필요한 것도 아니기 때문입니다. 오히려 이런 식으로 모든 요소를 전부 순회해야 하는 경우에는 기존 코드 방식보다 람다식의 성능이 더 떨어진다고 알려져 있습니다.
(2) 디버깅이 어렵다.
람다는 기본적으로 익명함수 기반이기 때문에, 익명 함수의 특성 일부를 따릅니다. 익명 함수의 특성상 함수 콜 스택 추적이 매우 어렵습니다.
에러가 발생하도록 아래와 같은 예를 만들어보겠습니다.
List<Integer> list = Arrays.asArray(1,2,3);
for(Integer i : list){
for(int j=0; j<i; j++){
System.out.println(i/j);
}
}
그리고 람다로 표현했을 경우에는 아래와 같습니다.
list.forEach(i -> {
IntStream.range(0, i).forEach(j -> {
System.out.println(i/j);
});
});
위 두 코드를 실행했을 때 콘솔에는 아래와 같이 출력됩니다.
람다 코드가 훨씬 많은 로그가 출력되는 것을 확인할 수 있습니다. 이는 람다가 내부적으로 수행하는 작업이 더 많기 때문이며, 코드가 복잡해지면 복잡해질수록 어디에서 문제가 발생했는지 확인하기가 어려워집니다. 이렇듯
내부적으로 수행하는 작업이 많기 때문에 위에서 말했듯이 상대적으로 성능이 떨어지게 됩니다. (최근에는 장비의 성능이 뛰어나기 때문에 큰 차이가 나지 않는다고 말합니다.)
참고. intellij의 경우 java stream plugin 을 제공하므로, 각 단계별로 결과 확인이 가능합니다.
결론
무조건적으로 람다가 옳은 것은 아닙니다. 프로그래밍 언어의 추세가 점차 함수형 프로그래밍 쪽으로 가는 형국이지만, 그렇다고 해서 무조건 함수형이 옳다거나 함수형 스타일로 코딩해야 하는 것은 아닙니다. 적재적소에 필요한 방법을 사용하는 것이 가장 바람직할 것입니다.
[references]
https://miromannino.com/blog/java-8-lambdas-limitations-closures/
'Java' 카테고리의 다른 글
Java(JVM) Memory Model(Runtime Data Areas) (0) | 2020.10.21 |
---|---|
Static 사용을 피해야 하는 이유 (1) | 2020.10.20 |
Lambda 무엇이 좋을까? (0) | 2020.10.20 |
자바7 업데이트 - 숫자 리터럴 구분자 (0) | 2020.10.20 |
[Refactoring] if문 (0) | 2020.10.20 |
댓글