JAVA - Lambda Capturing
목차
Lambda Capturing
먼저 람다식(Lambda Expressions)은 JDK 8 버전에서 추가된 기능으로 함수형 프로그래밍에서 보이는 arrow(화살표) 식으로 표현되는 문법으로 함수의 흐름을 명확하고 간결하게 표현하는 특징이 있습니다. 보통 데이터를 추출할 때 반복되거나 필터링이 필요할 경우 자주 사용됩니다. Java 에서 람다식은 함수로 취급되기 때문에 컴파일러가 .class 파일을 생성하지 않는다는 특징이 있습니다.
기본적으로 자바에서 람다식을 표현할 땐 (Parameter) -> function 의 구조로 이루어져 있으며, 파라미터로 넘긴 변수를 활용하여
작업을 수행합니다. 이때 람다 캡처링(lambda capturing) 은 파라미터로 넘겨받은 데이터가 람다식 내부에서 받은 변수가 아닌,
외부에서 정의된 변수(자유 변수 free vaiable)를 람다식 내부에 사용하는 행위를 의미한다.
public class lambdaCapturing {
int a = 123;
public void test() {
int b = 456;
b = 4;
Thread thread = new Thread(() -> {
@Override
public void run() {
System.out.println(a);
// 컴파일 에러 : "Variable used in lambda expression should be final or effectively final"
System.out.println(b);
}
});
}
}
위에 예제 코드를 보면 a라는 변수는 전역변수로 a를 정의하고 다시 재정의가 되었다. 그리고 thread lambda 에서 사용하게 된다면?
컴파일전부터 Error message 로 "Variable used in lambda expression should be final or effectively final" 를 보게 된다.
제약 조건
자유 변수를 람다식에 사용하려면 몇 가지 제약조건이 있습니다.
- 자유 변수(지역 변수 등)가 final 로 선언되어 있어야 한다.
- final로 선언이 되어있지 않다면 final 처럼 재할당이 되어서는 안된다.
- 람다식의 값은 단 한번만 할당되는 지역변수만을 캡처할 수 있다.
왜 이런 제약조건이 생겼을까요? 이유는 JVM과 관련이 있습니다. JVM의 메모리는 인스턴스 변수등을 힙에 저장하고 쓰레드끼리 공유가 가능하며, 지역변수는 스택에 저장합니다. 그리고 스택 영역은 쓰레드마다 별도의 스택이 생성되므로 지역 변수는 쓰레드끼리 공유가 되지 않습니다.
예를 들어 지역 변수를 캡처하는 람다가 있는 메서드가 있고 이 메서드가 종료될 경우 JVM은 람다식에 포함된 지역변수까지 할당을 해제합니다. 하지만 람다는 이미 참조한 지역변수를 문제없이 사용할 수 있는데 이는 직접 참조가 아닌 참조값을 복사하여 사용하기 때문입니다. 그러므로 지역변수의 할당이 해제되어도 람다 내부의 변수는 유지되며, 이 때문에 복제품의 값이 변경되지 않아야 복사된 변수의 값이 유지되므로 이런 불변성 제약 조건이 생겨났다고 합니다. 그래서 람다식 내부에 복제변수는 원본이 해제되어도 존재하기 때문에 자유 변수라고 합니다.
멀티 쓰레드환경에서 쓰레드풀을 사용해 람다식으로 정의하여 사용하던중 발생한 컴파일에러를 알아보다가 람다 캡처링을 알게되어 정리해봤습니다.