자바 람다(Lambda) 기초
페이지 정보
- 조회 18
- 작성일 2026.03.04 14:14
안녕하세요! 오늘은 자바 프로그래밍의 꽃이라고 불리는 '람다(Lambda)'에 대해 이야기해 보려 합니다.
아마 자바를 공부하다 보면 -> 처럼 생긴 화살표 기호를 본 적이 있으실 텐데요.
"대체 이게 뭐야?" 싶었던 분들을 위해, 아주 쉽고 친절하게 설명해 드릴게요.
1. 람다(Lambda)가 대체 뭔가요?
한마디로 정의하자면, 람다는 '이름이 없는 함수(익명 함수)'입니다.
우리가 보통 메서드를 만들 때는 이름도 붙이고, 반환 타입도 쓰고, 복잡한 절차를 거치죠?
하지만 람다를 사용하면 이런 번거로운 절차를 싹 생략하고, "기능 그 자체"를 변수처럼 다룰 수 있게 됩니다.
왜 쓸까요?
-
가독성: 코드가 획기적으로 짧아집니다.
-
생산성: 복잡한 익명 클래스 구현을 한 줄로 끝낼 수 있습니다.
-
병렬 처리: 뒤에서 배울 스트림(Stream) API와 결합하면 대용량 데이터 처리도 아주 쉬워집니다.
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));
-
fruit라는 이름으로 요소를 하나씩 받아서(->), 출력해라! 라는 뜻입니다. 훨씬 직관적이죠?
③ 조건에 맞는 데이터만 골라내기 (Stream Filter)
리스트에서 특정 조건(예: 글자 수가 3개 이상인 것)만 뽑아낼 때도 람다는 빛을 발합니다.
[실전 코드 예시]
List<String> names = Arrays.asList("김철수", "이영희", "박지성", "제임스");
// '김'씨 성을 가진 사람만 필터링해서 출력하기
names.stream()
.filter(name -> name.startsWith("김"))
.forEach(name -> System.out.println(name));
-
filter안에 들어간 람다식이 "이 조건에 맞는 애들만 통과시켜!"라는 문지기 역할을 합니다.
④ 새로운 스레드(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) (파라미터)->{가공}
결론
람다는 현대 자바 개발자에게 선택이 아닌 필수입니다.
처음에는 -> 기호가 낯설 수 있지만, 몇 번 직접 타이핑해 보면 이만큼 편한 도구도 없다는 걸 깨닫게 될 거예요.
오늘 배운 "(파라미터) -> {행동}" 구조만 기억해도 절반은 성공입니다!
이제 여러분의 프로젝트에 람다를 적용해 코드를 다이어트시켜 보세요.
출처 및 참고:
-
Oracle Java Documentation - Lambda Expressions
-
Baeldung - Introduction to Java 8 Lambda Expressions
- 이전글RedisSession 실제 구현 26.03.06
- 다음글Redis Session 개념 및 로직 설명 26.03.01
댓글목록
등록된 댓글이 없습니다.