자바에서의 람다식이나 익명 함수에서는
final
이거나 effectively final
인 외부 지역 변수만 접근할 수 있습니다.
하지만 코틀린의 람다식이나 익명 함수에서는 굳이 final
이 아니더라도 (val
이 아니더라도)
var
로 선언된 지역 변수에 접근할 수 있는 것을 볼 수 있습니다.
자바에서는 안 되는데 어떻게 코틀린에서 할 수 있는 것일까요?
fun interface TestInterface {
fun test()
}
위와 같은 `SAM Interface가 존재할 때
fun main() {
var count = 0
val test = TestInterface {
count++
}
test.test()
test.test()
println(count)
}
위와 같은 코드가 실행이 됩니다.
TestInterface
를 람다식으로 구현할 때
람다식 내부에서 지역 변수인 count
를 수정하는 것이 가능합니다.
자바에서는 실패하지만 코틀린에서는 성공합니다.
위 main
함수를 자바로 디컴파일 해보면 대충 다음과 같이 변경됩니다.
public final class MainKt {
public static final void main() {
IntRef intRef = new IntRef(0);
TestInterface test = () -> intRef.element++;
test.test();
test.test();
System.out.println(intRef.element);
}
}
이렇게 자바로 변경할 경우 지역 변수를 IntRef
라는 객체로 감싸고
객체의 내부를 변경하는 식으로 우회하는 것을 알 수 있습니다.
자바에서 이렇게 코드를 작성하게 되면 지역 변수가 Thread-safe
하지 못하기 때문에
이렇게 작성하는 것은 위험합니다.
만약 멀티 스레드 환경에서 람다식을 사용할 때에는 외부 지역 변수를 참조할 때
반드시 val
로 선언된 변수만 참조하는 것을 권장합니다.
코틀린의 람다식에서 외부 지역 변수를 변경할 수 있는 이유는
외부 지역 변수를 객체로 감싸서 우회했기 때문입니다.
단, 외부 지역 변수가 Thread-safe
하지 않은 상태에 놓이기 때문에
멀티 스레드 환경에서 조심해야 합니다.