람다 vs 익명 클래스
자바에서 람다와 익명 클래스는 모두 간단하게 기능을 구현하거나 일회성으로 사용할 객체를 만들 때 유용하지만 사용 방식과 의도에 차이가 있다.
1. 문법 차이
1) 익명 클래스
> 클래스를 선언하고 즉시 인스턴스 생성
> 반드시 new 인터페이스명() {...} 형태로 작성하며 메서드를 오버라이드해 구현
> 익명 클래스도 하나의 클래스
2) 람다 표현식
> 함수를 간결히 표현하는 방식
> 함수형 인터페이스(메서드가 하나인 인터페이스)를 간단히 구현할 때 주로 사용
> '->' 연산자로 표현하며 매개변수와 실행 내용을 간결히 작성 가능
> 람다도 인스턴스가 생성됨
2. 코드의 간결함
1) 익명 클래스 : 문법적으로 더 복잡하고 장황하며 코드의 양이 상대적으로 많음
2) 람다 : 간결하며 불필요한 코드를 최소화함
3. 상속 관계
1) 익명 클래스
> 일반적 클래스처럼 다양한 인터페이스와 클래스를 구현하거나 상속 가능
> 여러 메서드를 가진 인터페이스를 구현할 때도 사용할 수 있음
2) 람다 표현식
> 함수형 인터페이스만을 구현할 수 있음
> 클래스를 상속할 수 없으며 상태나 추가적인 메서드 오버라이딩은 불가능
4. 호환성
1) 익명 클래스 : 자바 오랜 버전도 사용 가능
2) 람다 표현식 : 자바 8버전 이후부터 사용 가능
5. this 키워드 의미
1) 익명 클래스
> this는 익명 클래스 자신을 카리킴
2) 람다 표현식
> this는 람다를 선언한 클래스의 인스턴스를 가리킴
> 즉, 람다 표현식은 별도의 컨텍스트를 가지는 것이 아니라 람다를 선언한 클래스의 컨텍스트 유지
OuterMain.java
package lambda.lambda6;
public class OuterMain {
private String message = "외부 클래스";
public void execute() {
// 1. 익명 클래스 예시
Runnable anonymous = new Runnable() {
private String message = "익명 클래스";
@Override
public void run() {
// 익명 클래스에서의 this는 익명 클래스의 인스턴스를 가리킴
System.out.println("[익명 클래스] this : " + this);
System.out.println("[익명 클래스] this.class : " + this.getClass());
System.out.println("[익명 클래스] this.message : " + this.message);
}
};
// 2. 람다 예시
Runnable lambda = () -> {
// 람다에서의 this는 람다가 선언된 클래스의 인스턴스(= 외부 클래스)를 가리킴
System.out.println("[람다] this : " + this);
System.out.println("[람다] this.class : " + this.getClass());
System.out.println("[람다] this.message : " + this.message);
};
anonymous.run();
System.out.println("--------------------------");
lambda.run();
}
public static void main(String[] args) {
OuterMain outer = new OuterMain();
System.out.println("[외부 클래스] : " + outer);
System.out.println("--------------------------");
outer.execute();
}
}
실행 결과

-> 람다에서 사용한 this와 외부 클래스의 인스턴스 참조값이 같음
-> 익명 클래스는 자신의 클래스와 인스턴스가 별도로 존재함
6. 캡쳐링
익명 클래스 & 람다 표현식 : 외부의 지역 변수를 캡쳐해 사용 가능하지만 final, 사실상 final 지역 변수만 접근 가능
"사실상 final"
영어로 effectively final이라 함, 지역 변수에 final 키워드를 사용하지는 않았지만 값을 변경하지 않는 지역 변수를 말함
7. 생성 방식
1) 익명 클래스
> 새로운 클래스를 정의해 객체를 생성하는 방식으로 컴파일 시 새로운 내부 클래스로 변환됨
> 예를 들어 OuterClass$1.class와 같이 이름이 지정된 클래스 파일이 생성됨
> 클래스가 메모리 상에서 별도로 관리되므로 약간의 추가 오버헤드 발생
2) 람다 표현식
> 내부적으로 invokeDynamic 메커니즘을 사용해 컴파일 타임에 실제 클래스 파일을 생성하지 않고 런타임 시점에 동적으로 필요한 코드 처리
> 람다는 익명 클래스보다 메모리 관리가 더 효율적이며 생성되 클래스 파일이 없어 복잡성도 줄어듬
람다 표현식의 작동 방법 예시
* 원본 코드
public class FunctionMain {
public static void main(String[] args) {
Function<String, Integer> function = x -> x.length();
System.out.println("function1 = " + function.apply("hello"));
}
}
* 컴파일 코드
public class FunctionMain {
public static void main(String[] args) {
Function<String, Integer> function = 람다 인스턴스 생성(구현 코드는 lambda1() 연결)
System.out.println("function1 = " + function.apply("hello"));
}
// 람다를 private 메서드로 추가
private Integer lambda1(String x) {
return x.length();
}
}-> 컴파일 단계에서 람다를 별도 클래스로 만드는 것이 아니라 private 메서드로 만들어 숨김
=> 람다가 약간의 메모리와 성능의 이점이 있지만 아주 미미하기에 실무에서 익명 클래스와 람다 성능 차이는 거의 없다고 보면 됨
8. 상태 관리
1) 익명 클래스
> 인스턴스 내부에 상태(필드, 멤버 변수)를 가질 수 있음
> 익명 클래스 내부에 멤버 변수를 선언하고 해당 변수 값을 변경하거나 상태를 관리할 수 있음
2) 람다 표현식
> 내부에 상태를 가지지 않고 기능만 제공
> 람다는 기본적으로 필드가 없으므로 상태를 유지하지는 않음
9. 익명 클래스와 람다의 용도 구분
1) 익명 클래스
> 상태를 유지하거나 다중 메서드를 구현할 때
> 기존 클래스 또는 인터페이스를 상속하거나 구현할 때
> 복잡한 인터페이스 구현이 필요할 때
2) 람다
> 상태를 유지할 필요가 없고 간결함이 중요할 때
> 단일 메서드만 필요한 간단한 함수형 인터페이스를 구현할 때
> 더 나은 성능(미미)과 간결한 코드가 필요할 때
'인프런 > 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍' 카테고리의 다른 글
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 8. 스트림 API 1 - 기본 (3) | 2025.06.14 |
|---|---|
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 7. 메서드 참조 (0) | 2025.05.28 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 5. 람다 활용 (0) | 2025.05.22 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 4. 함수형 인터페이스 (0) | 2025.05.21 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 3. 람다 (0) | 2025.05.12 |