람다 캡쳐링의 경우 저 역시 람다를 사용하기 막 시작했을 때 겪어 봤던 문제입니다.
코드의 경우 lamdaTest 리스트에 1 ~ 5의 값을 넣은 뒤 값이 3보다 크면 var에 1을 할당 한 뒤 반환해주고 3보다 작으면 var의 원래 값인 0을 넣어주는 로직입니다.
로직 자체는 예시를 위해 만든 것이니 약간 불필요한 변수를 선언하거나 로직 자체가 별로여도 이해 부탁드립니다ㅎㅎ
그럼 의도한 결과 값은 [0, 0, 0, 1, 1]이 되게 됩니다.
하지만 코드를 보시면 람다 내부에 var에 경고줄이 그어져 있는 것을 확인할 수 있습니다.
경고문의 내용은 아래와 같습니다
Variable used in lambda expression should be final or effectively final
그럼 이 경고문의 의미는 무엇일까요?
해석하자면 다음과 같습니다.
람다 표현식 내부에 사용된 변수의 경우 final이거나 effectively final(초기화 된 이후 값이 한번도 변경되지 않음)이어야만 한다
왜 저런 경고문이 나오게 될까요?
결론부터 말씀드리면 아래와 같습니다.
지역 변수는 Stack 메모리 영역에 저장되기 때문에 람다식에서 값을 바로 참조하는 것에 제약이 존재한다.
따라서 값 복사본을 사용하는데, 이 때 멀티스레드 환경에서 복사 되거나 복사된 값이 변경 가능할 경우 동시성 이슈가 발생할 가능성이 존재하기 때문이다.
그럼 지금부터 결론의 의미를 확인해보도록 하겠습니다.
(1)의 경우 에러발생
(2), (3)의 경우 정상
외부 변수로 지역 변수를 이용하는 람다식을 의미합니다. 위 변수 사용 예제에서 (1)의 경우가 되겠습니다.
외부 변수로 지역 변수를 이용하는 람다식의 경우 몇가지 특징이 있습니다.
요약하자면 다음과 같습니다.
익명 내부 클래스의 인스턴스를 생성할 때, 해당 익명 내부 클래스에서 사용된 어떠한 변수든 자동 생성된 생성자에 의해 각자의 복사본을 가지고 있다.
이러한 동작은 컴파일러가 옳바른 상태의 지역 변수를 바라보지 않고 자동생성 된 다양한 타입의 로직 내 논리성(상태가 존재하는?)이 존재하는 지역 변수를 바라 보는 것을 방지할 수 있기 때문이다.익명내부클래스의 인스턴스 내부에 복사된 지역변수의 경우, 다른 메소드에 의해 값의 변경이나 수정이 발생하게 되면 컴파일러 입장에서는 적절한가에 대한 논리적 의문성이 발생 가능하다.
이런 경우에는 다른 시간(스레드와 같은 동작으로 이해)에 사용된 복사본을 가지고 동작했었기 때문에 구식(이미 사용이 완료된 혹은 스택 내에 더 이상 존재하는 스레드 내 지역변수로 이해함)이 된 변수를 가지고 동작하는 코드가 존재할 수 도 있다.변수를 final 타입으로 변경하게 되면 이런 문제들을 제거할 수 있다.
즉, 값을 아에 변경되지 못하도록 한다면, 이런한 값 변경의 가시성에 대해 걱정하지 않아도 된다.