Kotlin Lambda final이 아닌 외부 지역 변수 접근

wannabeking·2023년 6월 22일
1

Kotlin

목록 보기
1/1
post-thumbnail

지난 포스팅에서 Java Lambda는 final 혹은 effectively final이 아닌 외부 지역 변수를 사용할 수 없다는 것을 정리했습니다.

그렇다면 코틀린에서는 어떨까요?

위 자바 코드와 동일한 기능을 하는 코틀린 코드를 작성해보겠습니다.


놀랍게도, 코틀린에서는 Lambda에서 final이 아닌 외부 지역 변수의 사용이 가능합니다.

어떻게 가능한 것일까요?



Kotlin Lambda에서 final이 아닌 외부 지역 변수 사용이 가능한 이유

그 이유는 코틀린에서 final이 아닌 변수를 capturing한 경우에는 변수를 특별한 래퍼로 감싸서 나중에 변경하거나 읽을 수 있게 한 다음, 래퍼에 대한 참조를 람다 코드와 함께 저장하기 때문입니다.

감싸진 변수를 "람다 캡처(captured lambda variable)"라고도 함


동작 과정을 조금 더 자세하게 살펴보면,

  1. 변수를 캡처할 때 해당 변수의 값을 저장할 래퍼 객체가 생성되고, 이 래퍼 객체는 람다가 변수를 참조할 수 있도록 하고 변수의 변경을 추적함

  2. 람다 표현식 내부에서 변수를 참조할 때, 실제로는 래퍼 객체에 저장된 값을 읽어옴, 래퍼 객체는 람다 표현식이 정의된 범위에서 캡처된 변수의 값을 유지함

  3. 변수의 값이 변경되면 래퍼 객체에 저장된 값도 함께 변경됨, 이후에 람다 표현식이 변수를 참조할 때는 변경된 값을 읽어옴

위와 같습니다.


즉, var 변수를 capturing 하면 실제로는 변수를 Ref 클래스 인스턴스에 넣고, 그 Ref 인스턴스에 대한 참조를 final로 만들어 capturing 합니다.

람다 안에서는 Ref 인스턴스의 필드를 변경할 수 있고 결국 numOfNames++은 실제로는 wrapper.setValue(wrapper.getValue() + 1) 과 같은 동작을 수행하게 됩니다.


하지만 람다 표현식을 병렬로 수행할 때 동시성 문제가 발생할 수 있으므로 여러 동기화 기법을 사용해 처리해야 합니다. (예를 들어 synchronized, AtomicInteger)


사실 자바에서도 위와 비슷한 방법으로, 원소가 단 하나뿐인 배열을 선언하거나 변경 가능한 변수를 필드로 하는 클래스를 선언하는 것으로 final이 아닌 변수 사용이 가능함



profile
내일은 개발왕 😎

0개의 댓글