
effectively final은 Java 람다를 쓰다가 지역 변수를 바꾸려 할 때 자주 만나는 규칙입니다. 변수에 `final`을 붙이지 않았는데도 컴파일러가 final처럼 다루는 상황이 생깁니다.
핵심은 람다가 캡처하는 지역 변수는 선언 후 다시 대입되지 않아야 한다는 것입니다. 이 글은 Java Language Specification과 Oracle Lambda 튜토리얼을 기준으로 왜 이런 제약이 있는지 정리합니다.

effectively final이란 무엇인가
지역 변수에 `final`을 직접 붙이지 않았더라도, 선언 후 값을 다시 대입하지 않으면 effectively final로 볼 수 있습니다. 람다에서 캡처하는 지역 변수는 final 또는 effectively final이어야 합니다.
String prefix = "Hello";
Runnable task = () -> System.out.println(prefix);여기서 `prefix`는 `final` 키워드가 없지만 값을 다시 대입하지 않았으므로 람다 안에서 사용할 수 있습니다.
다시 대입하면 왜 오류가 날까
int count = 0;
Runnable task = () -> {
System.out.println(count);
};
count = 1; // count는 더 이상 effectively final이 아님지역 변수는 메서드 실행 흐름 안에 있는 값입니다. 람다가 이 값을 캡처해서 나중에 실행될 수 있기 때문에, Java는 지역 변수 캡처를 단순하고 안전한 규칙으로 제한합니다.
람다 안에서 값을 증가시키면 안 되는 이유
가장 흔한 실수는 반복이나 집계 중에 람다 안에서 바깥 변수를 바꾸려는 코드입니다.
int total = 0;
numbers.forEach(n -> {
// total += n; // 오류
});이런 코드는 명령형 반복문으로 쓰거나, stream의 `sum`, `reduce`, `collect`처럼 값을 반환하는 방식으로 바꾸는 편이 더 자연스럽습니다.
대안 1: 결과를 반환하는 흐름으로 바꾸기
int total = numbers.stream()
.mapToInt(Integer::intValue)
.sum();람다 안에서 바깥 상태를 바꾸지 않고, stream pipeline이 결과를 만들어 내게 하면 effectively final 제약을 피하면서 코드 의미도 더 분명해집니다.
대안 2: 반복문이 더 읽기 쉬운 경우
int total = 0;
for (int n : numbers) {
total += n;
}모든 반복을 람다로 바꿀 필요는 없습니다. 상태를 단계적으로 바꾸는 코드라면 전통적인 for문이 더 솔직하고 디버깅하기 쉬울 수 있습니다.
AtomicInteger 우회는 신중해야 한다
가끔 `AtomicInteger`나 배열을 사용해 값을 우회적으로 바꾸는 코드를 볼 수 있습니다. 하지만 단순히 컴파일 오류를 피하려는 목적으로 쓰면 의도가 흐려집니다.
AtomicInteger total = new AtomicInteger(0);
numbers.forEach(n -> total.addAndGet(n));동시성 제어가 필요한 상황이 아니라면 이 방식은 과한 선택일 수 있습니다. 람다 안에서 바깥 상태 변경이 필요하다는 사실 자체를 먼저 의심하는 편이 좋습니다.
실무 체크리스트
- 람다 안에서 쓰는 지역 변수가 선언 후 다시 대입되는지 확인한다
- 상태 변경이 목적이면 for문이 더 읽기 쉬운지 본다
- 집계가 목적이면 sum, reduce, collect를 먼저 검토한다
- AtomicInteger는 동시성 의도가 있을 때만 신중히 사용한다
- 람다를 쓰기 위해 코드 구조를 억지로 꼬지 않는다
정리
Java의 effectively final 규칙은 람다에서 지역 변수를 안전하고 단순하게 캡처하기 위한 제약입니다. 값을 바꾸고 싶을 때는 우회보다 반복 구조나 반환형 흐름을 먼저 다시 보는 편이 좋습니다.
Java 문법 흐름은 자바 try-with-resources는 왜 필요할까, 자바 제네릭 와일드카드 extends super 차이, 자바 stream과 for문 차이와 함께 보면 좋습니다. 외부 기준은 Java Language Specification 15.27.2 – Lambda Body, Java Language Specification 4.12.4 – final Variables, Oracle Java Tutorial – Lambda Expressions를 확인했습니다.