자바 람다식에서 외부변수 참조가 가능한 이유

점돌이·2023년 8월 23일
0
post-thumbnail

람다식

public class PostDao implements Dao {
	@Override
    public <T> int add(T entity) throws SQLException {
        final Post post = (Post) entity;
        return databaseContext.execute(c -> {
            PreparedStatement ps = c.prepareStatement("insert into post (title, content, user_id) values (? , ? , ?)");
            ps.setString(1, post.getTitle());
            ps.setString(2, post.getContent());
            ps.setLong(3, post.getMember().getId());
            return ps;
        }).getRow();
    }
}

람다식은 인터페이스에 구현가능한 메소드가 1개만 있는 경우 따로 객체구현 없이 간편하게 구현 가능한 기능이다.
그간 람다식은 Stream API를 쓰면서 손에 익은대로 쓰고 있었는데 이번에 공부하며 의문이 생겼다.
지역변수는 각 스택영역에 올라가 함수가 끝나면 사라져 참조가 불가능한데 람다식은 다른 객체 안에 함수인데 어떻게 외부변수가 참조가능한지 말이다.
그 의문에서 출발한 글을 써보려 한다.

자바에서 함수지향 프로그래밍

람다식은 자바스크립트와 같이 함수지향 프로그래밍 언어에서 파생된 개념이다.
람다식의 편의성과 수준 높은 추상화를 보며 객체지향 프로그래밍 언어 자바도 1.7이후 버전에서 받아들이게 된다.
의문을 풀기위해서는 클로저라는 개념을 이해해야 한다.

클로저(Closure)

함수와 그 함수가 참조하는 외부 변수(자유 변수)들을 하나의 단위로 묶는 개념
즉, 함수의 실행 환경을 유지하는 개념

클로저의 개념은 함수의 환경을 기억하는것이라고 한다.

그렇다면 자바도 뭔갈 기억하는게 있겠단 감이 온다.

불변인 변수만 참조가능

        final Member member = (Member) entity;

위의 코드에서 변수부분만 가져왔다.

외부변수인 member에 재할당을 막는 final 키워드가 붙어있다.

외부변수는 다른 곳에서도 수정이 가능하니 불변을 보장해주면 람다식 내에서 참조가 가능하다는것이다.
이는 예상가능한 값과 스레드 세이프를 위한 장치이다.

메모리상으로는 람다식은 객체화가 되어 힙영역에 올라가게 된다.
이 때 참조되는 외부변수의 값도 같이 힙영역에 올리게 된다.
이를 자바에서는 람다 캡처링(Lambda Capturing)이라고 부른다.
위에서 본 클로저의 개념과 상당히 흡사한걸 알 수 있다.
그렇다면 final만 될까? 자바에서는 편의를 위해 하나 더 제공해준다.
effectively final이다.
final 키워드를 안붙여도 이후에 변수에 값이 재할당이 안된다면 final로 인정해주는 일종의 슈가 신택스다.

사실 실제로 람다식이 객체화 되는 과정은 좀 더 복잡하다.
https://d2.naver.com/helloworld/4911107
해당 링크 하단 3분의 1지점에서 람다식이 어떤식으로 컴파일 되는지 잘 설명 되어있다

자유변수(Free variable)

람다식에서 참조하는 변수들을 자유변수라고 일컫는다.
자유변수에는 람다식 내부에서 참조하는 변수와 외부에서 참조하는 변수가 있다.
람다식 내부에서 참조하는 변수는 힙영역에 레퍼런스가 올라가고 외부 참조하는 지역변수는 스택영역에 있기 때문에 값만 복사해서 힙영역에 올린다.
또 자유변수 참조 유무를 가지고 나뉘는데, 자유변수가 없으면 Stateless Lambda라고 하고 있으면 Stateful Lambda라고 한다.

주의사항

람다식은 외부변수의 값을 복사해서 가지고 있다.
따라서 만약 값이 바뀌더라도 람다식에서 최신값을 반영하지 못하기 때문에 예상하지못한 결과를 가져올 수 있다.
예를 들면 배열의 경우 불변을 보장해주지 못한다.
이렇게 확실한 불변이 보장되지않는 경우에는 안쓰는것이 좋다.

요약

  1. 불변이 보장되면 참조 가능
  2. 람다식이 객체화 될 때 외부변수의 값을 같이 힙영역에 올림
  3. 따라서 불변이 보장안되는 변수를 다룰땐 유의할것
profile
감사합니다.

0개의 댓글