Forest Gump?

TIL) 람다 캡처링 원리 알기 (capturing lambda) 본문

카테고리 없음

TIL) 람다 캡처링 원리 알기 (capturing lambda)

code1010 2023. 7. 17. 21:55

 

코드를 짜다가 습관적으로 int로 변수를 선언 후 람다 식 내부에서 사용하려고 했지만, 컴파일 오류가 나왔다.

아 맞다 캡쳐링! 이라는 생각과 Atomic 클래스로 래핑시켜주려다가, 선언 안되는 이유가 기억이 안나

다시 한번 복기한다. 

 

코드는 다음과 같다. 

public void capturing(){

		List<String> lists = Arrays.asList("apple","banana","peach");

		int count = 0;

		lists.forEach(fruits -> {
				try {
					rabbitTemplate.convertAndSend(exchangeName, routingKey,"object");
					count++; // 컴파일 에러가 나온다. 
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		);
	}

 

 

 

람다 캡쳐링이란?

 

 

위 코드에서는 count에 대한 변수가 사용이 안된다. 이를 람다 캡쳐링 이라고 하는데 , 파라미터로 받은 변수가 아닌 다른 외부 변수를 참조하는 람다 바디에서 사용하는 행위이다. 람다 캡쳐링이 무조건 안되는건 아니지만, 제약 조건이 두가지가 있다. 

 

 

1)  지역변수는 final로 선언 되어야 한다.

 

2) final 이 아닌 변수는 재할당이 이뤄지면 안된다. 

 

 

 

설명할 예제 코드는 다음과 같다.

 

 

 

 

count0/1/2는 참조가 가능하지만, count3의 경우 컴파일 에러가 명시되어 있다. 

final 이 아닌 지역 변수에만 재할당이 이루어 지면, 람다 캡쳐링 위배가 되어 사용이 안되는걸 알 수 있다.

무조건적으로 참조가 안되는게 아닌, 어디까지나 제약조건이다. 그럼 이런 제약이 걸리는 이유가 뭘까? 

 

 

 

람다 캡쳐링 제약 원인

 

이건 JVM 메모리 구조와 관련이 있다. 

 

 

 

위에서 선언한 변수인 count0은 인스턴스 변수로 힙 영역에 생성되어 할당되므로 사용하는데 문제가 없지만, 

지역 변수 count3은 스택 영역에 생성되기 때문이다. 어느 영역에 선언되는게 왜 문제가 되지? 라고 생각이 들 수 있어서

예제를 들어봤다.  

 

위 예시 코드의 람다식인 foreach 메소드의 실행이 중지되었을때 람다 메소드 내 모든 지역변수의 할당은 해제된다.

다만 람다 내부에서 참조해서 쓰는데는 문제가 없는데, 이는 원본 지역변수를 복제한 데이터를 사용하기 때문이다. 

원본 지역 변수를 복제해서 참조하였기 때문에, 캡쳐링에 제약이 생긴다고 이해하면 편하다. 

이는 자연 변수(free variable) 이라는 단어를 알면 이해가 더 쉬울 수 있다 .

 

 

 

자연 변수란?

 

 

 

외부 지역변수를 내부에서 참조하는 변수를 자연변수라고 한다. 람다식에 종속되지 않고, 지역변수 데이터를 자유변수에 저장하고 참조하는 행위를 람다 캡쳐링이라고 한다. 외부 변수의 값을 변화 시킬 수 있는 일반적인 절차형 프로그래밍 패턴을 컴파일러가 문법 차원에서 방지 하는 특징이 있다. 하지만 스택 영역이 아닌 count0 같은 인스턴스 변수의 경우

제약이 존재하지 않으므로 주의해야한다!

 

 

정리

 

1. 람다식은 외부의 변수를 활용 할 수 있지만 지역 변수에 대해 불변성을 강요하여 캡쳐링의 제약이 생긴다.  

2. 스택 영역이 아닌 다른 영역의 변수는 제약이 없다.  

 

 

Reference

 

https://goldenrabbit.co.kr/2021/11/03/%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EC%99%80-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%8A%A4%ED%83%9C%ED%8B%B1-%EB%B3%80%EC%88%98-%EB%93%B1%EC%9D%80-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%9D%98-%EC%96%B4%EB%94%94/

https://bugoverdose.github.io/development/lambda-capturing-and-free-variable/