개요

https://mureut.com/79

https://mureut.com/79

람다 표현식(Lambda Expression)에서는 익명 함수와 같이 자유 변수(free variable) 를 활용할 수 있다. 자유 변수란 파라미터로 넘겨진 변수가 아니라 외부에서 정의된 변수인데, 아래의 코드에서 delimiter 변수를 자유 변수라 할 수 있다. 이와 같은 동작을 람다 캡처링(capturing lambda)라 부른다.

public String composeNames(List<Book> books) {
	String seperator = ",";

	return books.stream().map(book-> book.getAuthor()).collect(Collectors.joining(seperator));
}

상당히 유용해보이지만, 이러한 자유 변수에 대한 람다 캡처링도 제약이 있다.

람다는 인스턴스 변수와 정적 변수를 자유롭게 캡처해서 자신의 바디부분에서 참조할 수 있는데, 이 경우 지역 변수는 effectivly final variable 이어야 한다. 즉 명시적으로 final 키워드가 붙거나 final처럼 변경없이 사용해야 한다는 것이다. 그렇기에 다음과 같은 코드는 컴파일할 수 없는 코드이다.

public String composeNames(List<Book> books) {
	String seperator = ",";

	return books.stream().map(book-> book.getAuthor()).collect(Collectors.joining(seperator));

	seperator = ":";//final 키워드가 없더라도 final처럼 사용하지 않으면 문제가 된다.
}

어째서 이런 제약이 있는 것일까?

어째서 지역 변수에 이런 제약이 있는 것일까?

우선, 내부적으로 인스턴스 변수와 지역 변수는 저장되는 위치부터가 다르다.

그렇기에 람다에서 지역 변수에 바로 접근할 수 있다는 가정하에 람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접근하려 할 수 있다.

그렇기에 자바 구현에선 원래 변수에 접근을 허용하는 것이 아닌 자유 지역 변수의 복사본을 제공하기에 복사본의 값이 바뀌지 않아야 하고 그렇게 지역 변수는 한 번만 값을 할당해야 한다는 제약이 생기게 된 것이다.

🚀참고: 클로저(closure)

람다는 클로저의 정의에 부합할까? 원칙적으로 클로저란 함수의 비지역 변수를 자유롭게 참조할 수 있는 함수의 인스턴스를 가리킨다. 예를 들어 클로저를 다른 함수의 인수로 전달할 수 있다.