개발놀이터

자바 람다식에 대한 고찰 본문

Java

자바 람다식에 대한 고찰

마늘냄새폴폴 2021. 9. 23. 05:39

*람다식의 대한 고찰

람다식은 내 입장에서 다른 자바8 내용인 Optional과 LocalDateTime과 다르게 이해하기 상당히 까다로운 내용이었다. 내가 나름대로 이해한 내용을 기록으로 적어 남긴다

 

*stream API에서의 람다식
-사고의 시작

Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator().forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));



람다식 안에 있는 paramName은 어떻게 구분하지? 자바 코드는 뭘 가지고 판단해서 paramName을 지정하는 것이지? request.getParameter(paramName)은 paramName이 어디서 들어왔길래 paramMap에 put할 수 있는것이지?

이에 대한 해결은 스트림 API에서 번뜩 생각이 났다.
스트림 API에서 
Map<String, String> map = new HashMap<>();
map.values().stream().filter(x -> x.contains("m")).forEach(System.out::println);
여기서도 마찬가지 x는 뭔줄알고 m이 들어있는것을 식별해서 콘솔창에 띄워주지? 잘 보면 stream앞에있는 map.values()에서 그 값을 한정한다는 것을 볼 수 있다.
저기 있는 스트림 API를 해석하면 이렇게 된다. 
map.values에 있는 값들을 x에 넣고 반복문을 돌려서 "m"이 들어있는 x를 콘솔창에 띄워준다.

request.getParameterNames().asIterator().forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName))); 이 람다식은 이렇게 해석할 수 있다.
request.getParameterNames().asIterator에서 뽑아온 파라미터를 paramName에 저장해서 반복문을 돌려서 paramMap에 순차적으로 paramName을 키로하는 request.getparameter(paramname)의 벨류를 저장한다.

 

 

*함수형 람다식

스트림 API는 람다식에서도 비교적 이해하기 직관적이었다. 하지만 함수형 람다식은 좀 더 난해했다. () -> { } 이런 식으로 생긴 형태는 아무리 봐도 이해가 되지 않는 직관적이지 않은 표현이다. 처음에는 이해하지 못한 채로 그냥 하라는대로 코드를 적어내려갔다. 하지만 이해하지 않고 계속 코드를 적다보니(물론 계속 의심은 했다. 왜 이걸 이렇게 사용할까 왜 이렇게 사용할까..하면서 말이다) 번뜩 떠올랐다. 

 

-사고의 시작

@Bean
	public AuditorAware<String> auditorProvider() {
		return () -> {
			ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

			String loginId = (String) attr.getRequest().getSession().getAttribute("loginId");

			if (loginId != null) {
				return Optional.of(loginId);
			}
			else {
				return Optional.of("Anonymous");
			}
		};
	}

 

스프링 데이터 JPA의 Auditing을 공부하다 생긴 코드이다. 내가 알던 return은 특정 값을 다시 돌려주는 것으로 이해하고 있었는데 return 값으로 함수형 람다식을 사용한 것이다. 내가 알던게 맞다면 함수를 리턴한다는 얘기가 된다. 함수를 리턴할 수 있나? 잠시 혼란이 있었지만 나도 종종 함수를 리턴한다. 그럼 함수 안에서 또 return을 해주면 리턴값을 리턴하는 식의 논리가 이어진다. 여기서 나는 람다식을 다시한번 지긋이 바라봤다. 람다식의 철학이 뭘까? 왜 람다식이 등장했을까? 내가 내린 결론은 코드의 간소화이다. 스트림 API만 봐도 Enhanced for문을 사용하면 처리할 수 있는 것들을 람다식을 이용하여 코드의 간소화를 이루어냈다고 볼 수 있다. 요는 함수형 람다식은 함수를 만들 때 밖으로 뺄 일을 만들지 않는 것이다. 즉 코드의 간소화가 일어난 것이다. 위의 코드에서 원래대로라면 return 메서드명(); 하고 밖에다 메서드를 만들어야한다. 함수형 람다식은 그런 번거로움을 줄여준 것이라고 이해했다. 

 

 

*이중콜론 연산자 ( :: )

스트림 API를 사용하다보면 가끔 이런 연산자가 등장한다. forEach(System.out:;println) 내가 처음 이중콜론 연산자를 직면한 표현이었다. 처음엔 이게 뭔가 싶었다. 이것도 함수형 람다식과 마찬가지로 그냥 아무것도 모르고 하라는대로 코드를 적어내려갔다. 그러다 인텔리제이에서 제공하는 기능중 x -> something 의 문법을 x::something 으로 바꿔주는 기능을 발견했다. 이것을 보고 번뜩 떠올랐다. 저 둘은 같은 뜻이라는 얘기고 그럼 둘이 표현하는 방식만 다르지 같은 뜻을 가지고 있다는게 아닐까? 하고 말이다. 그래서 구글링 한 결과 내 예상이 맞았다.

x -> new MemberDto(x) == MemberDto::new

x -> String.valueOf(x) == String::valueOf

x -> System.out.println(x) == System.out::println

이런식으로 서로 치환할 수 있는 것이었다. 알고보니 별거 아니라는 생각이 잠깐 들었다. 

 

 

 

람다식은 아직도 아리송한 개념중 하나이다. 자바의 어떤 문법보다도 개인적으로 제일 난해하고 제일 직관적이지 못해서 이해하기 어려웠던 것 같다. 하지만 이제 조금씩 이해가 되고있는 것 같아서 기분은 좋다.