안녕하세요! 오늘은 자바 프로그래밍의 꽃이라고 불리는 **'람다(Lambda)'**에 대해 이야기해 보려 합니다.
아마 자바를 공부하다 보면 -> 처럼 생긴 화살표 기호를 본 적이 있으실 텐데요.
"대체 이게 뭐야?" 싶었던 분들을 위해, 아주 쉽고 친절하게 설명해 드릴게요.
1. 람다(Lambda)가 대체 뭔가요?
한마디로 정의하자면, 람다는 **'이름이 없는 함수(익명 함수)'**입니다.
우리가 보통 메서드를 만들 때는 이름도 붙이고, 반환 타입도 쓰고, 복잡한 절차를 거치죠?
하지만 람다를 사용하면 이런 번거로운 절차를 싹 생략하고, **"기능 그 자체"**를 변수처럼 다룰 수 있게 됩니다.
왜 쓸까요?
2. 아주 쉬운 예제로 비교해보기
백문이 불여일견! 우리가 숫자 두 개를 더하는 기능을 만든다고 가정해 봅시다.
기존의 방식 (익명 클래스)
람다가 없던 시절에는 인터페이스를 구현하기 위해 아래처럼 길게 코드를 짜야 했습니다.
// 숫자를 더하는 인터페이스가 있다고 가정
interface Calculator {
int sum(int a, int b);
}
// 실제 사용 시
Calculator cal = new Calculator() {
@Override
public int sum(int a, int b) {
return a + b;
}
};
람다를 사용한 방식
똑같은 기능을 람다로 표현하면 어떻게 될까요?
Calculator cal = (a, b) -> a + b;
어떤가요? 서너 줄이 넘던 코드가 단 한 줄로 줄어들었습니다.
(a, b)는 입력받을 재료(파라미터)이고, -> 뒤에 있는 a + b는 실제 수행할 행동(로직)입니다.
3. 실무에서는 어떻게 쓰일까요? (구체적 사례)
가장 대표적인 사례가 바로 리스트 정렬입니다.
문자열 리스트를 글자 길이 순서대로 정렬하고 싶을 때, 예전에는 복잡한 비교기(Comparator)를 만들어야 했지만 지금은 다릅니다.
List<String> names = Arrays.asList("Apple", "Banana", "Kiwi");
// 람다를 활용한 정렬
names.sort((s1, s2) -> s1.length() - s2.length());
System.out.println(names); // 결과: [Kiwi, Apple, Banana]
Tip: 람다는 아무 곳에서나 쓸 수 있는 건 아닙니다.
**'함수형 인터페이스'**라고 해서, 추상 메서드가 딱 하나만 있는 인터페이스에서만 사용할 수 있어요.
자바에서는 @FunctionalInterface 어노테이션으로 이를 보장하곤 합니다.
4. 람다 작성 시 주의사항
람다가 편하다고 해서 모든 곳에 남발하는 것은 좋지 않습니다. 신뢰성 있는 코드를 위해 다음 세 가지를 기억하세요.
-
가독성이 최우선: 람다식이 너무 길어지면(예: 10줄 이상), 오히려 코드를 읽기 어렵게 만듭니다. 그럴 때는 그냥 별도의 메서드로 분리하는 것이 낫습니다.
-
디버깅의 어려움: 이름 없는 함수이기 때문에 에러가 발생했을 때 추적이 일반 메서드보다 조금 까다로울 수 있습니다.
-
지역 변수 제약: 람다 내부에서 외부의 지역 변수를 사용할 때는 그 변수가 사실상 수정 불가능(final)한 상태여야 합니다.
람다로 변신 가능한 메서드의 '자격 조건'
람다식을 사용하려면 해당 메서드가 속한 인터페이스가 반드시 **'함수형 인터페이스(Functional Interface)'**여야 합니다.
[ 함수형 인터페이스란? ]
추상 메서드(내용이 비어 있는 메서드)가 딱 하나만 존재하는 인터페이스를 말합니다.
만약 인터페이스에 구현해야 할 메서드가 두 개 이상이라면, 컴퓨터는 람다식이 어떤 메서드를 구현한 것인지 알 수 없기 때문에 에러가 발생합니다.
[ 어떤 구조를 줄일 수 있나요? ]
보통 다음과 같은 흐름을 가진 코드를 람다로 대체합니다.
-
입력(파라미터)은 있고 반환(Return)은 없는 구조: 데이터를 소비만 하는 경우 (Consumer)
-
입력은 없고 반환만 있는 구조: 데이터를 생성해서 주기만 하는 경우 (Supplier)
-
입력과 반환이 모두 있는 구조: 데이터를 받아서 가공한 뒤 돌려주는 경우 (Function)
-
입력을 받아 참/거짓(boolean)을 반환하는 구조: 필터링 조건으로 사용하는 경우 (Predicate)
[ 람다 변환 공식 ]
복잡한 메서드 구조에서 딱 핵심 재료만 남긴다고 생각하면 쉽습니다.
-
메서드 이름과 반환 타입을 지웁니다. (이름이 없어도 하나뿐인 메서드라 알아서 찾아갑니다.)
자바 컴파일러는 이미 인터페이스를 통해 이 메서드가 무엇인지 알고 있습니다. 따라서 중복되는 정보는 다 지워줍니다.
접근 제어자 (public, private 등) → 삭제
반환 타입 (void, int 등) → 삭제
메서드 이름 → 삭제
-
파라미터의 타입을 지웁니다. 파라미터가 2개 이상일 때 괄호 ()는 유지합니다. (컴파일러가 문맥을 보고 추론합니다.)
-
남은 파라미터와 실행 코드 사이에 **화살표(->)**를 꽂아줍니다.
5. 람다, 실전에서는 이렇게 씁니다! (실무 사례 보충)
① list 정렬 (sort)
람다식을 사용해서 리스트를 정렬할 수 있습니다.
[비포: 전통적인 compare 메소드 구현]
List<Integer> list = new ArrayList<>(List.of(5,10,3,6,1,4,9,8,7,2));
list.sort(new Comparator<Integer>() {
public int compare(Integer i1, Integer i2) {
return i1.compareTo(i2);
}
});
[애프터: 람다 활용1]
List<Integer> list = new ArrayList<>(List.of(5,10,3,6,1,4,9,8,7,2));
list.sort((i1,i2)->i1.compareTo(i2));
[애프터: 람다 활용2]
List<Integer> list = new ArrayList<>(List.of(5,10,3,6,1,4,9,8,7,2));
list.sort((i1,i2)->{
return i1.compareTo(i2);
});
- sort 의 파라미터는 Comparator 인데, 이 클래스틑 compare 메소드가 단 하나 존재합니다.
이 때 compare의 파라미터를 위와 같이 단순하게 줄여 사용할 수 있습니다.
② 리스트의 모든 요소 출력하기 (forEach)
기존에는 리스트 안에 있는 내용을 하나씩 꺼내 보려면 for 문을 돌려야 했죠.
하지만 람다를 쓰면 리스트에게 직접 "야, 이거 하나씩 출력해!"라고 명령할 수 있습니다.
[비포: 전통적인 for-each 문]
List<String> fruits = Arrays.asList("사과", "바나나", "포도");
for (String fruit : fruits) {
System.out.println(fruit);
}
[애프터: 람다 활용]
List<String> fruits = Arrays.asList("사과", "바나나", "포도");
fruits.forEach(fruit -> System.out.println(fruit));
③ 조건에 맞는 데이터만 골라내기 (Stream Filter)
리스트에서 특정 조건(예: 글자 수가 3개 이상인 것)만 뽑아낼 때도 람다는 빛을 발합니다.
[실전 코드 예시]
List<String> names = Arrays.asList("김철수", "이영희", "박지성", "제임스");
// '김'씨 성을 가진 사람만 필터링해서 출력하기
names.stream()
.filter(name -> name.startsWith("김"))
.forEach(name -> System.out.println(name));
④ 새로운 스레드(Thread) 만들기 (Runnable)
자바에서 병렬 처리를 위해 스레드를 만들 때, 예전에는 익명 클래스라는 복잡한 구조를 써야 했습니다. 람다를 쓰면 "할 일"만 딱 전달하면 됩니다.
[비포: 복잡한 익명 클래스]
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("작업시작");
}
});
thread.start();
[애프터: 람다 활용]
Thread thread = new Thread(() -> System.out.println("작업시작"));
thread.start();
초보자를 위한 핵심 요약
람다를 사용할 때는 다음을 기억하세요.
1) (파라미터)->결과
2) (파라미터)->{return 결과}
3) (파라미터)->{가공}
결론
람다는 현대 자바 개발자에게 선택이 아닌 필수입니다.
처음에는 -> 기호가 낯설 수 있지만, 몇 번 직접 타이핑해 보면 이만큼 편한 도구도 없다는 걸 깨닫게 될 거예요.
오늘 배운 "(파라미터) -> {행동}" 구조만 기억해도 절반은 성공입니다!
이제 여러분의 프로젝트에 람다를 적용해 코드를 다이어트시켜 보세요.
출처 및 참고: