Backend
home

람다

생성 일시
2025/05/22 14:14
태그
Java
게시일
2025/05/22
최종 편집 일시
2025/05/22 15:12

1. 람다

1.1 람다의 도입 배경

AI와 빅데이터 세상이 오면서, 큰 데이터를 잘 다루는 것이 매우 중요해졌다. 하드웨어도 발전했기에 멀티 코어 프로세서의 발전으로 잘 활용한다면 거대한 작업을 나누어 수행할 수 있게 되었다.
자바 컬렉션은 이미 강력했지만 테라바이트 급 혹은 거의 무한한 크기의 데이터 셋을 다루기엔 불편한 점이 많았다.
웹 어플리케이션 세상에서 큰 위치를 차지하고 있던 자바에게도 데이터들을 더욱 잘 다루기 위한 변화가 필요해졌다. 이전의 자바는 하나의 코어만을 사용했고, 나머지 코어를 사용하긴 쉽지 않았다.
이러한 맥락에서 병렬성의 활용과 간결한 코드를 위해, 자바 8 이후 아래 기술들이 도입되고 강화되었다.
1. 스트림 API
2. 인터페이스의 디폴트 메서드
디폴트 메서드를 활용해 컬렉션을 강화하였고, 거대한 컬렉션을 분산 환경에서 다루기 위한 병렬화 기술이 강화되었다. 그리고 이 컬렉션을 좀 더 효율적으로 다루기 위해 스트림이 강화되었고, 스트림을 편리하게 사용하기 위해 선언형-함수형 프로그래밍이 도입되었다. 추가적으로 선언형-함수형 프로그래밍을 위해 람다가 도입되었다. 결국 컬렉션을 효율적으로, 편리하게 “잘” 다루는 것이 최종 목표다.
데이터셋을 보다 복잡하게 다루는 상황이 많아지면서, 기존의 자바에서 사용하던 방식은 꽤나 불편하게 느껴졌다. for문을 이용해 순회하며, 내부적으로는 if문을 사용하고 또 그 안에서 for문을 사용하고, 분기하고… “무엇을” 하려는지 보다 “어떻게” 하는지에 집중할 수밖에 없었다.
이런 불편함을 해결함과 동시에 스트림을 보다 편리하게 사용하기 위해 도입된 것이 선언형-함수형 프로그래밍이다. 스트림을 사용한다면 이 자료구조로 “무엇을” 할지만 이야기하고, “어떻게”할지에 대한 이야기는 따로 생각할 수 있다.
SQL과도 같은 선언형적인 프로그래밍을 위해 람다가 도입되었고, 람다를 위해 함수형 인터페이스가 도입되었다.
람다의 도입으로 인해 자바는 객체지향언어인 동시에 선언형-함수형 언어가 되었다. 새로운 패러다임이 추가된 것이다.

1.2 람다

람다”식”은 메서드를 하나의 “식”으로 표현한 것이다.
그리고 람다란 코드 블록이다.
람다식의 예시를 살펴보자.
람다식을 적용하면 메서드의 이름과 반환값이 없어진다.
그래서 람다를 이름이 없다는 의미로 익명함수라고도 부른다.
마치 익명 객체를 익명 객체라고 부르는 것과 비슷한데 용도도 비슷하다.
다양한 예시를 통해 확인해보자.

1.2.1 람다와 메서드를 람다로 바꾸는 예시들

람다는 아래와 같은 구조를 갖는다.
(매개변수 목록) -> {로직}
Java
복사
우리가 알고 있는 메서드의 정의와는 조금 다르다.
1.
메서드 이름이 없다.
2.
반환 타입이 없다.
다른 예시를 보자. 일반 메서드를 람다로 표현해보자.
void foo() { System.out.println("lambdadi lambdadi lambdadida"); }
Java
복사
위와 같은 메서드를 다음과 같이 나타낼 수 있다.
() -> { System.out.println("lambdadi lambdadi lambdadida"); };
Java
복사
이름 “foo”와 반환 타입 “void”를 제거했다. 그리고 매개변수 리스트를 괄호 안에 표현했는데, foo()는 매개변수가 없으니 ()로 나타냈고, 매개변수 리스트와 메서드 바디는 → 화살표로 이었다.
로직이 한 줄인 경우 중괄호도 없앨 수가 있다.
() -> System.out.println("lambdadi lambdadi lambdadida");
Java
복사
딱 봐도 "lambdadi lambdadi lambdadida" 를 출력한다 라고 쉽게 알 수 있다.
다른 예시도 보자.
만약 리턴값이 있는 메서드였다면, 중괄호를 지우면서 ‘return’을 지울 수 있다.
또한 타입이 확실하다면 매개변수의 타입을 지울 수 있다.
int max(int a, int b) { return a > b ? a : b; }
Java
복사
위와 같은 메서드를 아래와 같이 나타낼 수 있다.
분명 return 타입을 명시하지 않았지만 Java의 Type Inference로 추론이 가능하다.
a와 b는 int임이 확실하니, 반환도 int겠구나…
(int a, int b) -> { return a > b ? a : b; }
Java
복사
그리고 중괄호를 없애며 아래와 같이 return을 지울 수 있다.
(int a, int b) -> a > b ? a : b
Java
복사
깔끔하다. 뭘 하는지 쉽게 알 수 있다.
잘 보면 문장 가장 뒤에 세미콜론이 없다. 중괄호까지 없애는 경우엔 세미콜론을 붙이지 않는다.
이는 람다 표현”식”이기 때문이다.
또 하나, 매개변수 타입이 추론 가능하다면 아래와 같이 매개변수 타입도 생략이 가능하다.
(a, b) -> a > b ? a : n
Java
복사
타입을 명시해야 코드가 더 명확한 경우나, 컴파일러가 타입을 판단할 수 없는 경우가 아니라면 람다의 모든 매개변수 타입은 생략하는 것이 좋다. 특히 매개변수 이름이 매우 긴 경우에 유용하다.
매개변수가 하나라면 아래와 같이 표현할 수도 있다.
(a) -> a * a a -> a * a
Java
복사
괄호를 없앤 것이다. 스트림에선 이런 표현을 자주 쓰게 될 것이다.
List<String> yeah = names.stream() .filter(name -> name.startsWith("Minsung") .toList();
Java
복사
이름 하나하나를 확인하며 어떤 수행을 하는 코드이다. 이때 중간의 람다식을 보면 “name”에 괄호가 없다는 사실을 알 수 있다.
람다를 적극적으로 활용하면 메서드를 다양한 방식으로 좀 더 간결하게 나타낼 수 있다. 우리가 평소에 사용하는 메서드들을 정의하기 위해 이런 람다를 사용하는 것은 아니다. 익명 객체와 똑같다. 위 코드처럼 한번 쓰고 버릴 메서드가 있다면, 람다로 간단하게 정의해 사용할 수 있다. 람다가 없는 경우 위와 같이 name.startWith을 호출하는 어떤 메서드를 정의하고, 필요한 경우 그 메서드를 가진 객체를 정의해서 사용해야 했을 것이다.

1.2.2 람다로 복잡한 Enum 리팩토링

람다를 활용해 복잡한 Enum을 리팩토링 하는 케이스를 살펴보자. 아래와 같은 복잡한 상황도 람다와 함께 리팩토링 하면 간결해진다.
Enum 상수마다 조금은 다른 동작을 보여야 하는 apply라는 메서드가 있다고 해보자. Operation class는 상수에 따라 다른 계산 방식을 보여야 한다. 이를 아래와 같이 간결하게 리팩토링 할 수 있다.

참고