본문 바로가기
language/java

Java 람다 표현식(Lambda) 완벽 이해하기

by 죄니안죄니 2026. 5. 27.
반응형

Java 람다 표현식(Lambda) 완벽 이해하기

Java 8 이후 Java 스타일을 완전히 바꿔버린 핵심 기능이 바로:

Lambda Expression (람다 표현식)

입니다.

현대 Java에서는:

  • Stream API
  • Comparator
  • CompletableFuture
  • 이벤트 처리
  • 함수형 프로그래밍

전부 람다와 연결됩니다.

예:

 
list.forEach(x -> System.out.println(x));
 

이런 코드가 바로 람다입니다.

처음 보면:

“함수처럼 생겼네?”
 

싶지만,

실제로 람다는:

  • 함수형 인터페이스
  • 익명 클래스
  • JVM invokedynamic
  • Closure
  • Stream 내부 처리

까지 연결되는 매우 중요한 기능입니다.

이번 글에서는:

  • 람다란?
  • 왜 등장했는가
  • 익명 클래스와 차이
  • 함수형 인터페이스
  • JVM 내부 동작
  • 실무 사용 패턴

까지 깊게 정리해보겠습니다.


1. 람다(Lambda)란?

Lambda calculus 기반 개념.

Java에서 람다는:

메서드를 하나의 값처럼 전달하는 문법

입니다.


2. 가장 기본 예시

 
(a, b) -> a + b
 

의미:

두 값을 받아 더해서 반환
 

3. 왜 필요했을까?

Java 8 이전 문제부터 보겠습니다.


4. 예전 Java 방식

예:

 
//String은 내부에 Comparable 이미 구현되어 있음 
//public final class String implements Comparable<String> {}
//그래서 a.compareTo(b) 가능
Collections.sort(list,
    new Comparator<String>() {

        @Override
        public int compare(String a, String b) {
            return a.compareTo(b);
        }
    }
);

//객체비교인 경우 
//User는 age 꺼내서 비교
users.sort(
    new Comparator<User>() {
        @Override
        public int compare(User u1, User u2) {
            return Integer.compare(
                u1.getAge(),
                u2.getAge()
            );
        }
    }
);
 

5. 문제점

코드가:

너무 장황(verbose)
 

함.

특히:

작은 동작 하나 정의하려고
익명 클래스 전체 작성
 

해야 했음.


6. 람다 등장 후

 
Collections.sort(
    list,
    (a, b) -> a.compareTo(b)
);

//객체
users.sort(
    (u1, u2) ->
        Integer.compare(
            u1.getAge(),
            u2.getAge()
        )
);
 

훨씬 간단.

 

람다에서 한단계 더 간 3단계까지도 가능 (메서드 참조 + 유틸리티)

= 람다를 더 의도가 잘 보이게 만든 Comparator 팩토리 메서드

list.sort(
    Comparator.naturalOrder()
);
//혹은 String -> length 추출해서 비교
list.sort(
    Comparator.comparingInt(String::length)
);


//객체 속성비교 (user) -> user.getAge() 까지 넘김
users.sort(
    Comparator.comparingInt(User::getAge)
);

 

체이닝 기법으로 연결 가능

users.sort(
    Comparator.comparingInt(User::getAge)
              .thenComparing(User::getName)
);


체이닝이 가능한 이유는 Comparator 인터페이스 (람다나 3단계 이거랑 관계 없이 Comparator객체라서 가능)

Comparator에는

default Comparator<T> thenComparing(...)
 

가 정의되어 있음.


7. 핵심 아이디어

람다는:

“동작 자체를 전달”
 

하는 것.


8. 문법 구조

기본 형태:

 
(parameters) -> expression
 

또는:

 
(parameters) -> {
    statements
}
 

9. 예시

 
x -> x * 2
 

의미:

입력 x 받아서 2배 반환
 

10. 여러 파라미터

 
(a, b) -> a + b
 

11. 코드 블록도 가능

 
(a, b) -> {

    int sum = a + b;

    return sum;
}
 

12. 매개변수 타입 생략 가능

 
(String s) -> s.length()
 

 
s -> s.length()
 

가능.


13. 왜 타입 생략 가능할까?

컴파일러가:

타입 추론(Type Inference)
 

수행하기 때문.


14. 그런데 람다는 “아무 데나” 못 쓴다

매우 중요합니다.

람다는 반드시:

함수형 인터페이스(Function Interface)
 

필요.


15. 함수형 인터페이스란?

Functional interface 는:

추상 메서드가 하나만 있는 인터페이스

입니다.


16. 예시

 
@FunctionalInterface
interface MyFunction {

    void run();
}
 

17. 왜 하나만 가능할까?

람다는:

“어떤 메서드 구현인지”
 

명확해야 하기 때문.


18. 사용 예시

 
MyFunction f =
    () -> System.out.println("Hello");
 

19. 실제로는 무엇인가?

많은 초보자가 오해하는 부분.

람다는:

진짜 함수(function) 아님
 

입니다.

Java는 여전히:

객체지향 언어
 

입니다.


20. 실제론 인터페이스 구현 객체

즉 내부적으로는:

함수형 인터페이스 구현 객체
 

생성.


21. 익명 클래스와 비교

예전 방식:

 
new Runnable() {

    @Override
    public void run() {

    }
}
 

람다 방식:

 
() -> {

}
 

22. 핵심 차이

람다는:

불필요한 문법 제거
 

한 것.


23. JVM 내부 동작

매우 중요합니다.

람다는 내부적으로:

invokedynamic
 

사용.


24. 왜 중요한가?

예전 익명 클래스보다:

  • 메모리 효율
  • 성능
  • 클래스 생성 비용

개선 가능.


25. 익명 클래스 문제

익명 클래스는 실제로:

별도 .class 생성
 

가능.


26. 람다는 더 최적화 가능

JVM이:

런타임 최적화
 

하기 더 유리.


27. Closure(클로저) 개념

람다는 외부 변수 접근 가능.

예:

 
int x = 10;

Runnable r =
    () -> System.out.println(x);
 

28. 왜 가능할까?

람다가:

외부 변수 capture
 

하기 때문.


29. 그런데 제약 존재

매우 중요.

effectively final
 

이어야 함.


30. effectively final이란?

명시적 final 없어도:

값 변경 안 되는 변수
 

입니다.


31. 왜 제한할까?

람다 실행 시점과:

지역 변수 생명주기 차이
 

문제 때문.


32. 대표 함수형 인터페이스들

Java는 기본 제공 많이 함.


33. Function<T,R>

 
Function<String, Integer>
 

의미:

String 입력
Integer 반환
 

34. Predicate<T>

 
Predicate<User>
 

의미:

boolean 반환 조건 함수
 

35. Consumer<T>

 
Consumer<String>
 

의미:

입력만 소비
반환 없음
 

36. Supplier<T>

 
Supplier<User>
 

의미:

값 생성 공급
 

37. Stream API와 람다

람다가 폭발적으로 중요해진 이유.

예:

 
list.stream()
    .filter(x -> x > 10)
    .map(x -> x * 2)
 

38. Comparator와 람다

실무에서 엄청 많이 사용.

 
users.sort(
    (a, b) -> a.getAge() - b.getAge()
);
 

39. Method Reference

람다 축약 문법.

예:

 
System.out::println
 

40. 의미

 
x -> System.out.println(x)
 

축약.


41. 람다 장점


장점 설명
코드 간결 O
함수형 스타일 O
Stream 궁합 좋음 O
가독성 향상 O

42. 람다 단점


단점 설명
디버깅 어려움 O
과도 사용 시 가독성 저하 O
복잡 로직엔 부적합 O

43. 실무에서 자주 하는 실수

1) 람다 안에 복잡 로직 넣음

가독성 폭망 가능.


2) Stream + 람다 과용

디버깅 어려움.


3) effectively final 이해 부족

컴파일 오류 발생.


4) 람다 = 함수형 언어라고 착각

Java는 여전히 OOP 기반.


44. 람다 vs 익명 클래스 차이


항목 익명 클래스 람다
문법 장황 간결
this 의미 익명 객체 바깥 객체
클래스 생성 O 최적화 가능
JVM 처리 일반 클래스 invokedynamic

45. 핵심 흐름 요약

함수형 인터페이스
↓
람다 표현식 전달
↓
JVM이 구현 객체 생성
↓
실행
 

46. 가장 중요한 핵심 한 줄

람다는
“함수형 인터페이스 구현을 간결하게 표현하는 Java 문법”
 

입니다.


47. 정리

람다 표현식은 단순 축약 문법이 아닙니다.

실제로는:

  • 함수형 프로그래밍
  • Stream API
  • Comparator
  • invokedynamic
  • Closure
  • JVM 최적화

전체와 연결되는 Java 8 이후 핵심 기능입니다.

특히 실무에서는:

  • 함수형 인터페이스
  • effectively final
  • Method Reference
  • Stream API
  • 람다 가독성 관리

를 정확히 이해하는 것이 매우 중요합니다.

다음 글에서는:

익명 클래스 vs 람다

를 this 차이, capture 방식, bytecode 차이, JVM 내부 생성 구조까지 포함해서 깊게 정리해보겠습니다.

반응형

댓글