[Kotlin In Action] 5-3) 람다의 변수 포획

ERyukSa·2023년 6월 22일
0

Kotlin In Action

목록 보기
3/9
post-thumbnail

람다의 변수 포획

람다를 함수 안에서 정의하면 람다의 파라미터뿐 아니라 람다 앞에서 정의된 함수의 지역 변수까지 람다 안에서 사용할 수 있다.

자바와 다른 점은 final이 아닌 변경 가능한 외부 변수에도 접근 및 변경이 가능하다는 것이다.

예시를 보기 위해 forEach 함수를 사용해보자. forEach는 컬렉션(문자열, 배열도 가능)의 모든 원소에 대해 람다 식을 호출한다. 일반적인 for문과 같지만 좀 더 간결하다.

예시를 살펴보자.

fun countStar(input: String): Int {
	var counter = 0
    input.forEach {
    	if (it == '*') counter += 1
    }
    return counter
}

위 함수는 문자열에서 '*'의 개수를 센다. 람다 밖의 final이 아닌 지역 변수 count에 접근하여 값을 변경한다는 점에 주목하자.

이와 같이 람다 안에서 사용되는 외부 변수를 람다가 포획(capture)한 변수라고 한다.

변수 포획 방식

기본적으로 지역 변수의 생명주기는 함수가 반환되면 끝난다.

하지만 함수가 자신의 로컬 변수를 포획한 람다를 반환하면 함수와 지역 변수의 생명주기가 달라질 수 있다.

즉, 함수가 종료된 후에 반환된 람다 식을 호출하더라도 람다는 포획한 변수에 접근하며 정상 동작한다.

어떻게 이것이 가능할까? 변수를 포획했을 때 어떻게 컴파일 되는지 알아보자.

final(val) 변수 포획 방식

람다가 final 변수를 포획하면 컴파일 될 때 포획한 변수의 값이 복사되어 람다식 내부에 저장된다. 따라서, 같은 값이지만 실질적으론 다른 변수이다.

변경 가능한 변수(var) 포획 방식

var 변수를 포획할 때는 약간의 fake를 쓴다.

변경 가능한 변수 필드를 하나 갖는 특별한 wrapper 클래스를 이용하는데, 그 클래스의 인스턴스를 val로 선언하고 인스턴스의 참조를 람다 내부에 저장한다.

그러면 인스턴스는 final 변수이지만 내부 변수는 변경이 가능하므로 자유롭게 포획 하고 변경도 할 수 있다.

이러한 포획 방식을 코틀린으로 구현해보면 다음과 같다.

class Ref<T>(var value: T)
val counter = Ref(0)
val inc = { counter.value++ }

하지만 개발자가 작성할 때는 wrapper 클래스를 사용하지 않고 아래처럼 변수에 직접 접근하면 된다.

var counter = 0
val inc = { counter++ }

정리

  • 람다를 함수 안에서 정의할 때, 람다 이전에 정의한 변수를 람다 안에서 사용할 수 있는데 이것을 람다의 변수 포획이라고 한다.
  • 람다에 포획된 지역 변수는 함수가 종료된 후에도 람다 안에서 접근할 수 있다.
  • 왜냐면, 포획된 변수가 final 변수이면 컴파일 될 때 값이 람다 안에 복사되고, 안final 변수는 wrapper 클래스 인스턴스의 참조를 람다와 함께 저장하기 때문이다. (실제론 지역 변수가 아니지만)

0개의 댓글